Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having trouble putting real-world logic into the DDD domain layer

Tags:

Despite having studied Domain Driven Design for a long time now there are still some basics that I simply figure out.

It seems that every time I try to design a rich domain layer, I still need a lot of Domain Services or a thick Application Layer, and I end up with a bunch of near-anemic domain entities with no real logic in them, apart from "GetTotalAmount" and the like. The key issue is that entities aren't aware of external stuff, and it's bad practice to inject anything into entities.

Let me give some examples:

1. A user signs up for a service. The user is persisted in the database, a file is generated and saved (needed for the user account), and a confirmation email is sent.

The example with the confirmation email has been discussed heavily in other threads, but with no real conclusion. Some suggest putting the logic in an application service that gets an EmailService and FileService injected from the infrastructure layer. But then I would have business logic outside of the domain, right? Others suggest creating a domain service that gets the infrastructure services injected - but in that case I would need to have the interfaces of the infrastructure services inside the domain layer (IEmailService and IFileService) which doesn't look too good either (because the domain layer cannot reference the infrastructure layer). And others suggest implementing Udi Dahan's Domain Events and then having the EmailService and FileService subscribe to those events. But that seems like a very loose implementation - and what happens if the services fail? Please let me know what you think is the right solution here.

2. A song is purchased from a digital music store. The shopping cart is emptied. The purchase is persisted. The payment service is called. An email confirmation is sent.

Ok, this might be related to the first example. The question here is, who is responsible for orchestrating this transaction? Of course I could put everything in the MVC controller with injected services. But if I want real DDD all business logic should be in the domain. But which entity should have the "Purchase" method? Song.Purchase()? Order.Purchase()? OrderProcessor.Purchase() (domain service)? ShoppingCartService.Purchase() (application service?)

This is a case where I think it's very hard to use real business logic inside the domain entities. If it's not good practice to inject anything into the entities, how can they ever do other stuff than checking its own (and its aggregate's) state?

I hope these examples are clear enough to show the issues I'm dealing with.

like image 894
lasseschou Avatar asked Sep 05 '11 09:09

lasseschou


People also ask

What is domain logic DDD?

Domain-driven design. Using microservices means creating applications from loosely coupling services. The application consists of several small services, each representing a separate business goal. They can be developed and easily maintained individually, after what they are joint in a complex application.

What should domain layer contain?

The domain layer is a collection of entity objects and related business logic that is designed to represent the enterprise business model. The major scope of this layer is to create a standardized and federated set of objects, that could be potentially reused within different projects.

What is domain in DDD architecture?

Domain-Driven Design(DDD) is a collection of principles and patterns that help developers craft elegant object systems. Properly applied it can lead to software abstractions called domain models. These models encapsulate complex business logic, closing the gap between business reality and code.

What is domain logic vs application logic?

The main difference between them is that domain services hold domain logic whereas application services don't. As we discussed in a previous post, domain logic is everything that is related to business decisions.


2 Answers

Dimitry's answer points out some good things to look for. Often/easily you find yourself in your scenario, with a data shoveling from db up to GUI through different layers.

I have been inspired by Jimmy Nilsson's simple advice "Value objects, Value objects and more Value objects". Often people tend to focus to much on Nouns and model them as entity. Naturally you often having trouble in finding DDD behavior. Verbs are easier to associate with behavior. A good thing is to make these Verbs appear in your domain as Value objects.

Some guidance I use for my self when trying to develop the domain (must say that it takes time to construct a rich domain, often several refactoring iterations...) :

  • Minimize properties (get/set)
  • Use value objects as much as you can
  • Expose as little you can. Make you domain aggregates methods intuitive.

Don't forget that your Domain can be rich by doing Validation. It's only your domain that knows how to conduct a purchase, and what's required.

Your domain should also be responsible for validation when your entities make a transition from one state two another state (work flow validations).

I'll give you some examples: Here is a article I wrote on my blog regarding your issue about anemic Domain http://magnusbackeus.wordpress.com/2011/05/31/preventing-anemic-domain-model-where-is-my-model-behaviour/

I can also really recommend Jimmy Bogard's blog article about entity validations and using Validator pattern together with extension methods. It gives you the freedom to validate infrastructural things without making your domain dirty: http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/

I use Udi's Domain Events with great success. You can also make them asynchronous if you believe your service can fail. You also wrap it in a transaction (using NServiceBus framework).

In your first example (just brainstorming now to get our minds thinking more of value objects).

  1. Your MusicService.AddSubscriber(User newUser) application service get a call from a presenter/controller/WCF with a new User. The service already got IUserRepository and IMusicServiceRepository injected into ctor.
  2. The music service "Spotify" is loaded through IMusicServiceRepository
  3. entity musicService.SignUp(MusicServiceSubscriber newSubsriber) method takes a Value object MusicServiceSubscriber. This Value object must take User and other mandatory objects in ctor (value objects are immutable). Here you can also place logic/behavior like handle subscriptionId's etc.
  4. What SignUp method also does, it fires a Domain Event NewSubscriberAddedToMusicService. It get caught by EventHandler HandleNewSubscriberAddedToMusicServiceEvent which got IFileService and IEmailService injected into it's ctor. This handler's implementation is located in Application Service layer BUT the event is controlled by Domain and MusicService.SignUp. This means the Domain is in control. Eventhandler creates file and sends email.

You can persist the user through eventhandler OR make the MusicService.AddSubscriber(...) method to this. Both will do this through IUserRepository but It's a matter of taste and perhaps how it will reflect the actual domain.

Finally... I hope you grasp something of the above... anyhow. Most important is to start adding "Verbs" methods to entitys and making the collaborate. You can also have object in your domain that are not persisted, they are only there for mediate between several domain entities and can host algorithms etc.

like image 100
Magnus Backeus Avatar answered Jan 03 '23 02:01

Magnus Backeus


A user signs up for a service. The user is persisted in the database, a file is generated and saved (needed for the user account), and a confirmation email is sent.

You can apply Dependency Inversion Principle here. Define a domain interface like this:

void ICanSendConfirmationEmail(EmailAddress address, ...)

or

void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...)

Interface can be used by other domain classes. Implement this interface in infrastructure layer, using real SMTP classes. Inject this implementation on application startup. This way you stated business intent in domain code and your domain logic does not have direct reference to SMTP infrastructure. The key here is the name of the interface, it should be based on Ubiquitous Language.

A song is purchased from a digital music store. The shopping cart is emptied. The purchase is persisted. The payment service is called. An email confirmation is sent. Ok, this might be related to the first example. The question here is, who is responsible for orchestrating this transaction?

Use OOP best practices to assign responsibilities (GRASP and SOLID). Unit testing and refactoring will give you a design feedback. Orchestration itself can be part of thin Application Layer. From DDD Layered Architecture:

Application Layer: Defines the jobs the software is supposed to do and directs the expressive domain objects to work out problems. The tasks this layer is responsible for are meaningful to the business or necessary for interaction with the application layers of other systems.

This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down. It does not have state reflecting the business situation, but it can have state that reflects the progress of a task for the user or the program.

like image 34
Dmitry Avatar answered Jan 03 '23 04:01

Dmitry