0

Considering this pattern is used to support CQRS message bus, examples are buslane Python or MessageBus PHP

It uses commands to change the domain model, and publishes domain events

This looks great providing the separation, and encapsulating each domain write operation on its own classes, but doesn't that make an anemic domain model ? Can a domain model be thought of a collection of services, and objects ?

Even if, doesn't that results in a domain model that is just an entity, or a data container, and all the business logic is implemented in their own command handlers.

On the contrary, if all the commands which changes the model are implemented in a class, isn't that a kind of a god class ? Or doesn't it violate SRP ?

2 Answers 2

3

You are comparing apples to oranges. I think you may be either misunderstanding what the libraries to which you are referring above actually do, or you are misunderstanding DDD. Services/Commands/CommandHandlers operate at the application service "level". They are used to coordinate your domain and execute use cases. In this way, they provide a modularized way to interact with your domain (in contrast to scripts for example).

For example (taken directly from the docs of your latter option):

class RegisterUserCommandHandler
{
    ...

    public function handle(RegisterUser $command)
    {
        $user = User::register(
            $command->emailAddress(),
            $command->plainTextPassword()
        );

        $this->userRepository->add($user);
    }
}

The above conducts no business rules. As such we could imagine our User domain object like:

class User
{
    public static funtion register( string $emailAddress, string $plainTextPassword )
    {
        if( strlen($plainTextPassword) > 100 )
            throw new \PasswordIsTooLongException;

        ...

        DomainEvents::trigger( new UserRegistered($emailAddress) )
    }
}

In this way our domain is not anemic. That is, the rules can still be encapsulated by our domain.

0

Can a domain model be thought of a collection of services, and objects

Yes. An ADM + Services approach is actually a very good way of separating out operations where the only thing in common is the data they operate on.

ie If i have to Purchase an Order and Deliver an Order. instead of having two Order objects OrderForDelivery, OrderForPurchase etc I can have one Order with no methods and two services PurchaseService and DeliveryService.

Obviously lumping them all into one DoAnythingWithAnOrderService would be a mistake

Now You can see that the command pattern is very similar, but usually it is considered a pattern of itself

245
  • Playing Devil's Advocate, why is DoAnythingWithOrderService a mistake, but your alternative, DoAnythingWithPurchaseService, good?
    – user949300
    Commented Jan 8, 2019 at 7:01
  • why do you think thats my alternative? purchaseService is just order.purchase() moved out of order
    – Ewan
    Commented Jan 8, 2019 at 7:10
  • I disagree. While ADM + Services is a way, it is not a good way. The purpose of DDD (and OOP in general) is to reduce coupling and increase cohesion. This manifests itself as the encapsulation of behavior with the data it acts upon. Passing "objects" into "services" tends to create an immense amount of coupling because the public surface for your "objects" must be large. It also leads to either lots of duplication or an explosion of "rules" because validation is done around the data instead of with the data. Commented Jan 8, 2019 at 22:11
  • In your example you are confusing the domain-centric view of the Purchase and Deliver operations with the application-centric view. Order.Purchase/Deliver would likely only change some internal Status of the Order (as one's domain is only responsible for conducting changes of state). The OrderPurchased/Delivered events raised in the process would be consumed by other contexts to actually carry-out the payment/shipment. Commented Jan 8, 2019 at 22:11
  • Though, these may very-well take the form of Purchase/DeliverService! The key point here is that they are decoupled from the Order (i.e. you needn't actually have the Order object to pay for it. Just payment details, an amount, and an OrderId). In this way, we can greatly simplify the system. Commented Jan 8, 2019 at 22:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.