3

Most of my work is written in Java using Spring Boot. In a recent project my entities contained no business logic, Spring Data was used to handle repositories and I have service classes that controllers would call to retrieve certain data or perform actions on an entity. For example, you might have a UserService with a restPassword action which would handle resetting a users password.

I’ve read a lot about domain driven design lately and I’m getting confused because the examples in one book show behaviour on what looks like an entity. For example, a changeStatus() action on an Order.

If applying the MVC patter, I’m struggling to understand the relationships between controllers, services, DTOs, DAO and entities.

Let’s say your building a web service, your controller will return JSON to the client. Your entities would match the structure of your database, but you need another type of object to return the data to the client as the structure of that data could be different from your database, this is the DTO?

Would your controller talk to a service? And what would the service return? Entities or DTOs? If entities would you map those to DTOs using a mapper in the controller?

What about behaviour? Is that ok in a service class, or should entities be mapped to DAOs that contain behaviour?

I’m confused, when I research this, some appear to get entities, repositories and daos confused. Some say entities can contain behaviours, others day no.

Another example, say we want to know what permissions a user has in the system, we can find this by calling getUserPermissions(User user) which might return List. What would we call from inside a controller? Would we put this in a PermissionsService class and call that? Or would we have another object that represented an entity but which also had behaviour and could communicate with the repositories to gather additional data? We certainly couldn’t put it on the entity itself, as that would mean injecting a repository into an entity, which feels wrong.

Any guidance is appreciated.

3

Your primary starting points should be

  • Eric Evans: Domain Driven Design
  • Martin Fowler: Patterns of Enterprise Application Architecture

Evans offers the clear introduction to what it means to put the domain model first. Fowler summarizes the domain model pattern, along with several alternatives to it.

Here's the key idea - enterprises write custom software (rather than just buying something off of the shelf), because either the off-the-shelf-ware they need doesn't exist, or because being able to fine tune the details are important to the bottom line of the business.

If you are an enterprise with a JVM development team, you don't roll your own web server, or data access objects, or any of that truck that is the same that everybody else uses. You download Spring, or something else your development team likes, and invest your development capital in code that makes a difference.

So when Evans talks about "entities", he's not talking about rows in a database -- that's just plumbing. He is talking about concepts that are critical to the business model -- what does it mean to be a cargo shipment? If we get a message announcing that a container has been unloaded at the wrong port, or has missed a connection, what do we do about it?

And yes, absolutely, in the Evans/Fowler framing, the classes that represent domain entities do include methods that modify their own state. Tell, don't ask.

A repository class would be responsible for getting the OrderEntity from the db and returning it, but what class would be responsible for mapping it to an Order object on the domain?

Per Evans, chapter 6, that work would also happen within the repository -- more precisely, you would read the value (from hibernate), convert the value into an aggregate root using a Factory, and then return the root to the application.

(So the application code get to see the hibernate value at all, it is enclosed within the aggregate root).

  • Thanks, that has helped a little. Let's say I have an OrderEntity via hibernate. It's properties resemble the db structure. Then I have a class called Order which represents an Order in the domain. A repository class would be responsible for getting the OrderEntity from the db and returning it, but what class would be responsible for mapping it to an Order object on the domain? Do I create an OrderService with a getOrder(Long id) : Order that talks to the repository and maps the entity to the domain object? It this kind of structure/flow/responsibilities I'm struggling with. – SheppardDigital Sep 21 '18 at 6:36
1

It's important to understand your application in terms of it's architecture. I think your confusion is likely being caused by a lack of understanding as to why applications are structured the way there are. Starting with the basics, a traditional "layered" architecture, it will look something like:

View -> Application -> Domain -> Persistence 

The idea being that each layer is completely decoupled from the layers above it, such that the flow of dependencies in "downward". The management of dependencies and coupling is the most critical concern of any architecture! What the above accomplishes is the ability to "swap" out any layer in a way that we know what other pieces of the application may have to be changed as a result (the layer(s) directly above). With this powerful idea in mind, it makes sense to structure your dependencies in a way where:

Volatile Modules -> Stable Modules

Because your Domain is likely the least volatile module (almost certainly not going to be swapped out), we can use the DIP (dependency inversion principal) to "fix" the layered architecture above to:

View -> Application -> Domain <- Persistence

Now we have reached architecture you are describing in your application. Now let's go through each layer and figure out for what each should be responsible.

Your View layer is where your controllers live. Each Controller simply mediates the flow of data (and control) towards your Application layer. Importantly, your controllers don't contain business logic. They only contain logic necessary to transform input from the outside world into concepts your Application layer can understand, and transform the output from your Application layer into concepts the outside world can understand. If we "swapped" out your View layer (or added another one) from a web interface to command line interface, you would need new controllers. The inputs/outputs to the View would necessarily be different.

The Application layer is used to carryout use-cases for your users. It does this by coordinating your Domain. Again, it is important that this layer does not contain business logic. For example, for changing a password:

class ChangePasswordCommand
{
    private string newPassword;

    public string UserId { get; set; }

    public string CurrentPassword { get; set; }

    public string NewPassword
    { 
        get { return newPassword; } 
        set { 

            if( value.Length > 50 ) {
                throw new System.ArgumentException("New password must be less than 50 characters");
            }

            newPassword = value;
        }
    }
}


class ChangePasswordCommandHandler
{
    private IUserRepository userRepository;

    handle( ChangePasswordCommand cmd )
    {
        userId = cmd.UserId;
        newPassword = cmd.NewPassword;
        currentPassword = cmd.CurrentPassword;

        user = this.userRepository.FindById(userId);

        // this may raise a UserChangedPasswordDomainEvent
        // or throw a PasswordIsNotDifferentException
        user.changePassword(newPassword, currentPassword);

        this.userRepository.save(user)
    }
}

Notice there is no "logic" occurring here that pertains to the actual changing of a password. The invariant that the ChangePasswordCommand is protecting really has nothing to do with your business logic. That is, your CEO probably isn't worried about the length of a Users password. The password length invariant is a technical detail of the system (say your backing database field is VARCHAR(50) or something), and we want to "fail fast".

The command handler, itself, simply takes some input that it understands (ChangePasswordCommand), and coordinates your domain model to carryout the command. Things like transaction management should also occur in your Application layer. Think of every use-case as a 3-step problem:

  1. Get pieces of domain model into memory that are needed to carryout use-case.
  2. Coordinate domain model (change state) 2.5. Handle/bubble exceptions
  3. Persist domain model

It's also worth noting that the dependency on IUserRepository. This interface is defined in your Domain layer, but is implemented in your Persistence layer (this is the DI we added).

Following the flow of control, we reach your Persistence layer. This is where your ORM exists (and only here!). For example the UserRepository.FindById might look like:

public User FindById( string userId )
{
    // 1. get UserEntity
    // 2. map UserEntity to User
    // 3. return User
}

There is really no need for some extra DTO or anything unless you intend on using some library for mapping which requires a certain format. Again, the idea here is that this layer can be completely replaced and NO OTHER part of your application would even know the difference. The only vectors of change for this layer are when you change/update your ORM or the Domain.

Finally, we get you your Domain. This is where everything is happening. This is where we make sure that 2 passwords cannot be the same, and mutate the domain model accordingly. Not too much explanation necessary here.

Lastly, I'd like to make a note here that ALL OF THE ABOVE is really only necessary in the context of WRITING to a system. You can read from it however you want (there is no danger of an invalid state during a read). If you need user permissions, just get them. Don't over-complicate it. Get the data you need to your View however you want, BUT make sure you only write data through the proper channels (your Domain). This is the idea of CQRS.

You are free to create a separate read model project, but there is no concept of, or need for, a Domain in this project so it will end up being quite redundant. Best to just define some read services to organize it if you wish. I can understand the confusion of some Controller methods (reads) using one project, and other methods (writes) using another, but a View needs both.

  • Thanks, very helpful. A question on entities, if using an ORM you wouldn’t generally put bahaviour on the entity. What you then leave entities in the infrastructure layer, and create models in your domain layer that are mapped from entities but also constrain behaviour? For example, and Invoice, you may want a method on the Invoice object to return the invoice total by summing the value of line items. If this is the case, what is responsible for mapping entities to models in the domain layer? A domain repository that talks to the infrastructure repository? – SheppardDigital Sep 21 '18 at 21:23
  • Mapping should occur in the same layer as your repository. Repositories are only implemented as infrastructure (recall the flow of dependencies above). This mapping from your data model (ORM) to domain model could be done in each repository, or moved into its own cohesive mechanism. It’s actually not very important “how” mapping occurs as long as the Repository interface (defined in the domain layer) is met. Does that make sense? – king-side-slide Sep 23 '18 at 13:49
  • It’s does. I’m thinking from the perspective of a Spring application that uses hibernate. The repository uses Spring Data, so there would need to be some kind of mapping service between the domain layer and the repository in the infrastructure layer to map JPA enties to domain models. – SheppardDigital Sep 23 '18 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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