Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD (Domain Driven Design) Application Layer

I have been trying to build an application based on DDD but I have some questions.

Some layers that I have: - Presentation Layer - MVC - Application Layer - Domain Layer ...

First of all, I would like to know if I can do this in the ApplicationLayer (get family info > get default message info > send an email > updated the database):

public ApproveFamilyOutput ApproveFamily(ApproveFamilyInput input)
        {
            Family family = _familyRepository.GetFamily(input.Username);
            family.Approve();

            DefaultMessage defaultMessage = _defaultMessageRepository.GetDefaultMessage(MessageTypes.FamilyApproved);

            _email.Send(family.GetEmail(), defaultMessage.Subject, defaultMessage.Message);

            _familyRepository.Update(family);
            bool isSaved = _familyRepository.Save();

            return new ApproveFamilyOutput()
            {
                Errors = Helper.GetErrorIfNotSaved(isSaved)
            };
        }

Am I thinking well? Is the Application layer responsible to do that job?

The second question is: I need to send some data to the presentation layer according to the privileges that the user has. These privileges are defined in the database. Example: - The object Family have the Name, LastName, PhoneNumber, Email properties and the user can show/hide each of the values. How can I handle with this?

Can I do something like in the Application Layer:

public GetFamilyOutput GetFamily(GetFamilyInput input)
        {
            Family family = _familyRepository.GetFamily(input.Username);

            FamilyConfiguration familyConfiguration = _familyConfigurationRepository.GetConfigurations(family.Id);

            //ProcessConfiguration will set to null the properties that I cannot show
            family.ProcessConfiguration(familyConfiguration);

            return new GetFamilyOutput
            {
                //Map Family Object to the GetFamilyOutput
            };
        }

Note: The Family, DefaultMessage and FamilyConfiguration are domain objects created inside the Domain Layer.

What is your opinion?

Thanks :)

Edited: Note: I liked all the answers below and I used a little of all :) (I can´t mark all answers as acceptable)

like image 472
ASantos Avatar asked Mar 19 '17 01:03

ASantos


People also ask

What is DDD (Domain-Driven Design)?

Domain-driven design ( DDD) is a software design approach focusing on modelling software to match a domain according to input from that domain's experts. In terms of object-oriented programming it means that the structure and language of software code (class names, class methods, class variables) should match the business domain.

What is domain dependency in DDD?

Dependencies in a DDD Service, the Application layer depends on Domain and Infrastructure, and Infrastructure depends on Domain, but Domain doesn't depend on any layer. This layer design should be independent for each microservice.

What is DDD modeling?

Layers in DDD microservices Domain-driven design (DDD) advocates modeling based on the reality of business as relevant to your use cases. In the context of building applications, DDD talks about problems as domains.

What are DDDDD layers in the ordering microservice in eshoponcontainers?

DDD layers in the ordering microservice in eShopOnContainers The three layers in a DDD microservice like Ordering. Each layer is a VS project: Application layer is Ordering.API, Domain layer is Ordering.Domain and the Infrastructure layer is Ordering.Infrastructure.


3 Answers

What your application service is doing in #1 is perfectly valid: it coordinates the workflow with very little to no business logic knowledge.

There are certainly few improvements that could be done however, for instance:

  1. I do not see any transactions? The email should only be sent upon a successfully comitted transaction.

  2. Sending the email could be seen as a side-effect of the family's approval. I suppose the business experts could have stated: "when a family is approved then notify interested parties by email". Therefore, it could be wise to publish a FamilyApproved domain event and move the email sending logic in an event handler.

    Note that you want the handler to be called asynchronously only after the domain event was persisted to disk and you want to persist the event in the same transaction as the aggregate.

  3. You could probably further abstract the mailing process into something like emailService.send(MessageTypes.FamilyApproved, family.getEmail()). The application service wouldn't have to know about default messages.

  4. Repositories are usually exclusive to aggregate roots (AR), if DefaultMessage is not an AR then I'd consider naming the DefaultMessageRepository service differently.

As for #2, although authorization checks could be done in the domain, it is much more common to relieve the domain from such task and enforce permissions in the application layer. You could even have a dedicated Identity & Access supporting Bounded Context (BC).

"//ProcessConfiguration will set to null the properties that I cannot show"

That solution wouldn't be so great (just like implementing the IFamilyProperty solution) because your domain model becomes polluted by technical authorization concerns. If you are looking to apply DDD then the model should be as faithful as possible to the Ubiquitous Language (UL) and I doubt IFamilyProperty is something your domain experts would mention or even understand. Allowing properties to become null would probably violate some invariants as well.

Another problem with such solution is that the domain model is rarely adapted for queries (it's built for commands), so it's often preferrable to bypass it entirely and favor going directly to the DB. Implementing authorizations in the domain would prevent you from doing that easily.

For at least these reasons, I think it is preferrable to implement authorization checks outside the domain. There your are free to use whatever implementation you want and suits your needs. For instance, I believe that stripping off values from a DTO could be legitimate.

like image 144
plalx Avatar answered Sep 25 '22 01:09

plalx


I also was doubting if it's ok to place some logic to Application Service or not. But things got much cleaner once I read Vladimir Khorikov's Domain services vs Application services article. It states that

domain services hold domain logic whereas application services don’t.

and illustrates the idea by great examples. So in your cases I think it's totally fine to place these scenarios to Application Service as it doesn't contain domain logic.

like image 42
Zharro Avatar answered Sep 23 '22 01:09

Zharro


Ad 1
I usually move that logic into domain layer - services.
So then the application layer just calls:

public ApproveFamilyOutput ApproveFamily(ApproveFamilyInput input)
{
    var approveService = diContainer.Get<ApproveService>(); // Or correctly injected by constructor
    var result = approveService.ApproveFamily(input);

    // Convert to ouput
}

And domain service (AppproveService class) looks like:

public ApproveResult ApproveFamily(ApproveFamilyInput input)
{
     var family = _familyRepository.GetFamily(input.Username);
     family.Approve();

     _familyRepository.Update(family);
     bool isSaved = _familyRepository.Save();

     if(isSaved)
         _eventPublisher.Publish(family.raisedEvents);
     // return result
}

To make it work (and following hexagonal/onion architecture), domain layer defines all interfaces for its dependencies (IFamilyRepository, IDefaultMessageRepository, etc) and application layer injects specific implementation into domain layer.

To make it clear:
1. domain layer is independent
2. domain objects are pure - consist of entities, value objects
3. domain objects don't call repositories, it is up to the domain service
4. domain objects raise events
5. unrelated logic is handled by events (event handlers) - for example sending emails, it follows open-closed principle

class FamilyApprovedHandler : IHandle<FamilyApprovedEvent>
{
    private readonly IDefaultMessageRepository _defaultMessageRepository;
    private readonly IEmailSender _emailSender;
    private readonly IEmailProvider _emailProvider;

    // ctor

    public Task Handle(FamilyApprovedEvent event)
    {
        var defaultMessage = _defaultMessageRepository.GetDefaultMessage(MessageTypes.FamilyApproved);

        var email = _emailProvider.Generate(event.Family, defaultMessage.Subject, defaultMessage.Message);

        _emailSender.Send(email);
    }
}
like image 28
Marfu Avatar answered Sep 24 '22 01:09

Marfu