8

So after reading "Implementing Domain-Driven Design by Vaughn Vernon" I've decided to refactor my code for better re usability by isolating what I believe to be core domain concepts into separate modules.

Each module contains their own set of distinct architectural layers which include the Domain, Infrastructure and the Application/Presentation layer (per Vaughn's recommendation I've decided to further separate the responsibilities of the Application layer from routes, MVC controllers + templates which exist in the Presentation Layer).

I've decided to place each of these layers within their own package; and each package is referencing the layer beneath it as a dependency. ie: Presentation Layer depends on the Application Layer, Application depends on Infrastructure, etc. Since the Repository is a part of the Domain, each Repository Interface exists within the Domain layer/package with implementation being a responsibility of the Infrastructure layer/package (Doctrine, etc).

I'm hoping that by restructuring my code in this way I'll be able to swap out the Application layer and reuse my Domain across multiple Web Applications.

The code finally looks like it's starting to shape again however what still confuses me is this distinction between Application, Infrastructure and Domain Services.

One common example of a Domain Service is something which you would use to hash passwords. This makes sense to me from a SRP perspective since the User entity shouldn't concern itself with the many different hashing algorithms which might be used to store a user's credentials.

So with that in mind I treated this new Domain service the same way as my Repositories; by defining an interface in the Domain and leaving the implementation up to Infrastructure layer. However, now I'm wondering about what should be done with the Application Services.

As it stands now, each Entity has its own Application Service, i.e. the User Entity has a UserService within the Application Layer. The UserService in this case is responsible for parsing primitive data types and handling a common use-case "UserService::CreateUser(string name, string email, etc) : User.

What concerns me is the fact that I'll need to re-implement this logic across multiple applications should I decide to swap out the Application layer. So I guess this leads me to my next few questions:

  1. Are Domain Services merely an Interface which exist to provide a layer of abstraction between the Infrastructure Layer and your Model? ie: Repositories + HashingServices, etc.

  2. I mentioned having an Application Service which looks like this:

    • Access/Application/Services/UserService::CreateUser(string name, string email, etc): User

    • The method signature accepts primitive data type arguments and returns a new User Entity (not a DTO!).

    Does this belong in the Infrastructure layer as an implementation of some interface defined within the Domain layer or is the Application Layer in fact more appropriate due to primitive data type arguments, etc?

    example:

    Access/Domain/Services/UserServiceInterface 
    

    and

    Access/Infrastructure/Services/UserService implements UserServiceInterface
    
  3. How should separate modules handle unidirectional relationships. Should module A reference module B's application layer (as it's doing now) or the infrastructure implementation (via separated interface)?

  4. Do Application Layer Services require a Separate Interface? If the answer is yes then where should they be located?

1
  • 2
    These are very diverse issues. I suggest you split this into separate questions. Commented Mar 25, 2016 at 10:56

2 Answers 2

7

Are Domain Services merely an Interface which exist to provide a layer of abstraction between the Infrastructure Layer and your Model? ie: Repositories + HashingServices, etc.

Domain services responsibilities include several things. The most obvious is housing logic that doesn't fit into a single entity. For example, you may need to authorize a refund for a certain purchase, but to complete the operation you need data from the Purchase entity, Customer entity, CustomerMembership entity.

Domain services also my provide operations needed by the domain to complete its functionality such as PasswordEncryptionService, but the implementation of this service will reside in the infrastructure layer since it will mostly be a technical solution.

Infrastructure services are services which so an infrastructure operation such as opening a network connection, copy file from file system, talk to an external web service or talk to database.

Application services are the implementation of a use case in the application you are building. If you are cancelling a flight reservation you would:

  1. Query the database for the Reservation object.
  2. invoke Reservation->cancel.
  3. Save object to DB.

The application layer is the client of the domain. The domain has no idea what your use case is. It just exposes the functionality through its aggregates and domain services. The application layer however mirrors what you are trying to achieve by orchestrating the domain and infrastructure layers.

I mentioned having an Application Service which looks like this: Access/Application/Services/UserService::CreateUser(string name, string email, etc): User The method signature accepts primitive data type arguments and returns a new User Entity (not a DTO!).

PHP might not be the best place to start learning about DDD since many of the PHP frameworks out there (Laravel, Symfony, Zend,..etc) tend to promote RAD. They are focused more on CRUD and translating forms to entities. CRUD != DDD

Your presentation layer should be responsible for reading the form inputs from the request object and invoking the correct application service. The application service will create the user and invoke the User repository to save the new user. You may optionally return a DTO of the user back to the presentation layer.

How should separate modules handle unidirectional relationships. Should module A reference module B's application layer (as it's doing now) or the infrastructure implementation (via separated interface)?

The word module in DDD lingo has a different meaning than what you are describing. A module should house related concepts. For example, an order module in the domain layer could house the Order aggregate, OrderItem entity, OrderRepositoryInterface and MaxOrderValidationService.

An Order module in the application layer could house the OrderApplicationServie, CreateOrderCommand and OrderDto.

If you are talking about layers then each layer should preferably depend on interfaces of other layers whenever possible. The presentation layer should depend on interfaces of the application layer. Application layer should reference interfaces of the repositories or domain services.

I personally don't create interfaces for entities and value objects coz I believe interfaces should be related to a behavior, but YMMV :)

Do Application Layer Services require a Separate Interface? If the answer is yes then where should they be located?

It depends :) for complex applications I build interfaces coz we apply rigorous unit, integration and acceptance testing. Loose coupling is key here and the interfaces are in the same layer (application layer).

For simple app I build against the app services directly.

1
  • I agree for most of what you have written, just I want to say that the RAD part of the frameworks helps so much to prototype the application, for decoupling we can rely on Domain Modeling even if PHP is not well known with this, if we have an active record implementation as data source layer we can consider it as DTO as uncle bob said is another kind of DTO and an adapter can mediate between domain layer and the data source commands also are another kind of DTOs the best practice is to couple the forms to Command (DTO) not the entity, almost every framework has DI container so use interfaces. Commented Aug 13, 2016 at 15:15
1

Hmm short answer to a long question, but I see the pattern as follows

Rule 1 : Domain Objects should have a single Aggregate Root

Rule 2 : Aggregate Roots shouldn't be too big, Split things up into Bounded Contexts

Problem : Aggregate Roots soon become too big and there is no clear way to draw a line between the various Domain Models in them

Solution : Domain Services. Create interfaces you can inject into Domain Models that allow them to do things outside of their Domain Context or Aggregate Root.

So I think I would say that your examples are just normal Services/Repositories etc, IDatabaseRepositoryForStoringUsers or IGenericHashingCode

A Domain Service enables communication between Bounded Contexts. ie

User.UpgradeAccount(IAccountService accountService)
{
    accountService.UpgradeUsersAccount(this.Id);
}

Where Users and Accounts are in separate Aggregate Roots/Bounded Contexts.

If the User and Account are in the same Aggregate Root you should of course be able to do:

User.UpgradeAccount()
{
    this.MyAccount.Upgrade();
}

I'm not totally clear from your question how you are integrating the nTier Application/Infrastructure stuff and the Module stuff. Obvs you don't really want any cross referencing between Bounded Contexts, so you would put your Domain Service Interfaces in their own Module which doesn't reference any other Bounded Context. limiting you to exposing base value types, or perhapse DTOs only

2
  • Integration is a concern of each module's Application package/layer. ie: Service Container Dynamic Bindings + Routes. As for cross-module relationships - if another separated module (ProjectManagement for example) needs to perform some type of Authentication within it's own respective API than I include the "Access" module's Application layer as a listed dependency. The package manager then retrieves any remaining nested dependencies. Access Application -> Access Infrastructures -> Access Domain so I can perform the necessary work. Commented Mar 21, 2016 at 5:19
  • 1
    Yes I think that kind of cross domain linking is probably a mistake. You are likely to get circular dependencies
    – Ewan
    Commented Mar 21, 2016 at 8:04

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.