Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to model and handle presentation DTO's to abstract from complicated domain model?

Hi I am developing an application that needs to work with a complex domain model using Hibernate. This application uses Spring MVC and using the domain objects in the presentation layer is very messy so I think I should use DTO's that go to and from my service layer so that these match what I need in my views. Now lets assume I have a CarLease entity whose properties are not simple java primitives but it's composed with other entities like Make, Model, etc

    public class CarLease {
        private Make make;
        Private Model model;
        .
        .
        .
    }

most properties are in this fashion and they are selectable using drop down selects on the jsp view, each will post back an ID to the controller.

Now considering some standard use cases: create, edit, display

How would you go about modeling the presentation DTO's to be used as form backing objects and communication between presentation and service layers??

Would you create a different DTO for each case (create, edit, display), would you make DTO's for the complex attributes? if so where would you translate the ID to entity?

how and where would you handle validation, DTO/Domain assembly, what would you return from service layer methods? (create, edit, get)

As you can see, I now I will benefit by separating my view from the domain objects (very complex with lots of stuff I don't need.) but I am having a hard time finding any real world examples and best practices for this. I need some architecture guidance from top to bottom, please keep in mind I will use Spring MVC in case that may leverage on your anwser.

thanks in advance.

like image 690
arrages Avatar asked Apr 11 '10 20:04

arrages


3 Answers

For what it's worth (I'm developing in C# .net - but the principles should hopefully still be helpful to you) I've defined a bunch of types (DTO's) which I use to exchange data between the business and data tiers; and I have more than one type per domain object / entity. These are defined as simple classes.

Each of these is built with a specific task in mind, for example:

  • I have a light-weight type designed to populate list views (lots of "rows" but not many "columns"). I also have a corresponding collection type that holds any number of these.
  • I have a "big" get copy which usually has all the properties of the entity in question - but for only one instance of it. I may have more than one of these depending on the size and complexity of the entity vs the cases of use; and also depending on whether you want to return all the data associated with an instance of the entity straight away, or lazy-load some on later requests.
  • I also usually have separate "save" (new) and "update" types for the entity.

Each type is designed to hold only the information relevant for a given task. For example the "big" will return the date last modified, but I don't expect that in my save and update types because I populate those in the data access layer.

Also, for my app, these types exist in a common assembly - so they can be re-used between any tier, not just between the business and data tiers.

Architectural Fit

There's nothing particular special about this approach, it has it's own pros and cons; exactly what those are and how they affect you will depend on a lot of things - I guess your mileage will vary - but it's certainly served me well for a number of years now.

People often make a fuss over "separation of concerns" - and that's a really wise move; this relates to DTO's in that they are exchanged between layers (and services, components, etc) so there can always some ambiguity over where exactly to draw the line.

I take the approach that if a bit of information is fit to be exchanged between to tiers it's probably fit to be exchanged between any number of tiers - so why not make it accessible to all? It also saves have to re-cast information if you're just passing it through.

As far as complexity goes - there are two ways of handling that:

  1. Use a verbose / human readable naming convention for all; the types so you know what things are; it doesn't matter how many there are - that's what intelli-sense (& docs) are for. The more intuitive the better.
  2. KISS - keep things simple if you can; you'll have to balance sensible reuse and the Single Responsibility Principle (SRP).

Would you create a DTO of a complex property of a main entity?

I've found myself making DTO's for one of 2 reasons:

  1. There's data I know I need to expose (push), and the design of the DTO is a no-brainer: it's driven by the data I want to expose.
  2. Pull: the consumer know's what it wants, and the DTO is designed to meet those needs.

Because they are all defined in a common assembly no one component "owns" it, it helps force you to think from a 'domain' perspective rather than a component centric one; to an extent this will influence the design of the DTO's (balancing reuse vs SRP).

In both cases the DTO's made can be quiet specific to a particular need, or generic; e.g, A DTO that has only a int and a string is not uncommon, it's the sort of thing you'd use for sending to dropdownlists.

Most of the DTO collections I send back (from DAL to BL) are specific to a concept - not generic. I enforce very very basic rules on these via the constructors I offer: every arg is required. I'm not sure if this answers your question "How do you manage the assembly and validation".

like image 174
Adrian K Avatar answered Sep 20 '22 14:09

Adrian K


Have you considered a command query responsibility separation (CQRS) principle? In a nutshell, it is an architectural principle advocating using separate components for read and modify operations.

Modifications are done using commands sent to the Domain Model. Your NewCarLeaseDTO looks like a command -- CreateNewCarLeaseCommand. They contain all the data needed for a particular operation.

On the other hand, reads (either for list or detail vies) are done directly on underlying data store (SQL database). There are many possibilities here, ranging from having the same data store backing read and write parts to having two independent data stores connected via publish/subscribe infrastructure.

When using the latter (two stores) approach to read side, many (like Udi Dahan) advocate using so-called persistent views. It means storing data directly in the form consumable by your views. Transformation (denormalization) is done when synchronizing after model update.

If you wish to learn more about CQRS, I recommend reading Udi Dahan and Greg Young.

like image 32
Szymon Pobiega Avatar answered Sep 20 '22 14:09

Szymon Pobiega


The idea that the service layer should return DTO's instead of EJB objects is mostly a pre EJB3/JPA era idea. During CRUD you really gain a lot from working with the model objects (a.k.a. entities) directly.

You can however benefit from using DTO's when used for performance optimizations, for example when model objects are too bulky or when you gain from using some smart joins for aggregating model data.

So unless you are engineering under SOA, I would not recommend working with DTO’s for CRUD operations.

like image 37
Kdeveloper Avatar answered Sep 20 '22 14:09

Kdeveloper