I work on standard web application with domain organized around DDD concepts. I'm wondering what kind of objects my application services should accept and return. Let's say that I have an application service for User
aggregate.
1) DTOs / simple types (string, int etc)
public interface UserApplicationService {
void registerUser(UserDTO userDTO);
List<UserDTO> getUsersForOrganization(String organizationId);
}
In this case, application service is responsible for calling assembler transforming DTOs to domain objects and the other way round.
Advantage of this approach is that my application service is a clear boundary for my domain objects. Another one is that application service is a clear transaction boundary. Domain objects that are managed by persistence context do not leak somewhere outside of the transaction.
Disadvantage is that in case of forms, validation must be based on DTO. So my validation rules are duplicated between domain (object is responsible for its state) and DTO validation rules. (As in case of Spring MVC sample application). Also, if some parts of view requires another form of model (let's say that UserDTO has not enough information for view to be rendered), I'll need to create another DTO and basis on couple of DTOs returned from application service, compose another one, used by view.
2) Domain types
public interface UserApplicationService {
void registerUser(User user);
List<User> getUsersForOrganization(OrganizationId organizationId);
}
In this case, controller / presenter is responsible for transformation.
Big disadvantage is that my domain objects leak from application service - no clear separation. Also, where are our transaction boundaries? Domain objects that may be attached to, for example Hibernate session, leaks outside of application services layer. (However, I noticed that this is how many sample applications are written.)
Advantage may be that the controller / presenter is responsible for preparation of model for view, so it can compose DTOs basis on the view requirements. For instance, view may require some additional information that is not returned in DTO from #getUsersForOrganizationMethod. Also, validation may be based on domain objects, so it's not duplicated between DTOs and domain objects.
3) Domain objects + facade
This is third option used in DDDsample application. Application services return domain types, but there is some facade that is responsible for transformation. So in my case, controller / presenter talks to facade with DTOs, facade does the transformation and it talks with application services using domain objects. However, in my humble opinion it seems a little bit overwhelming - too many layers, too many boilerplate code. For one application service it may sound great, but if we had tens of them, we need to have the same number of facade methods - pure duplication. Additionally, where are transaction boundaries?
+1
I prefer a mixed solution.
1) I use domain objects as arguments, but limited to ValueObject s. I believe the lifecycle of Entity s should be managed carefully, and most of the time the view doesn't have enough value to populate the whole Entity except very simple CRUD applications. I saw many times that some developers init Entity by constructor carelessly and populate them with only part of the fields they required for a particular function, this makes it very easy to introduce bugs for NullPointerException and the poor guy who is assigned to fix the issue has to search across tens of methods to find where the Entity given was created. Entity s are retrieved from Repository or created by Factory in my projects.
Sometimes I use some form objects as parameter for simplicity.
2) I use a mvc Controller to convert domain objects returned by application service to ViewAdapter(a componenet decouples domain models from ui), sometimes dettach work need to be done here.
3) I use a Facade only when the application service need to be exposed via remote procedure call like web-service. Dto s are used as arguments and return types in this case and the Facade is responsible for converting DTO and Domain Model.
4) Validation is awkward if the application service need to be exposed to both web view and remote procedure call. This causes duplicate validation implemented on both form objects and Dtos. I validate simple constraints only(Not null, length to name a few, business rules are validated by domain objects programmatically) because I haven't found a perfect solution yet.
Hope this helps and if there are better solutions do let me know please.
Update1:
1) I have to confess that I'm not a guru in this area and I'm trying to find a good solution as well. So sometimes there is some inconsistency in my current solution such as the form bean you mentioned in comment. Sometimes we take some form bean as Command and place some domain logic in it, so these command belong to Domain Layer in this case.
2) Transaction boundary is on the application service. Technically, The domain object could be modified out of the boundary inadvertently. We cover this by team discipline and code review.
I usually find myself leaning towards approach one, using commands and queries.
Here is a snippet of a blog post I'm publishing this weekend.
Commands help you in supporting the ubiquitous language by explicitly capturing user intent at the boundaries of your system - think use cases. They serve as a layer over your domain, decoupling the inside from the outside, allowing you to gradually introduce concepts on the inside, without breaking the outside. The command executor gives you a nice pipeline you can take advantage of to centralize security, performance metrics, logging, session management and so on. And also, if that's your thing - commands can be serialized dealt with asynchronously.
You can find an example of this on my blog; http://www.jefclaes.be/2013/01/separating-command-data-from-logic-and.html.
In respect to your concerns regarding validation, keep in mind that having validation duplicated isn't horrible; http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With