Onion layer responsibilities

Overview

  • All dependencies are towards the center.
  • Interfaces are defined at the layer that uses them and implemented in outer layers.
  • Outermost layer is purely technical.
  • Domain layers contain pure business rules and business logic.
  • Domain model must be kept pure and without any dependencies. It can only depend on itself.

Domain layer

  • Business rules that are defined with business experts.
  • Definitions of business entities.
  • Be vigilant about not letting rules slip out of this layer into domain or application services layer, otherwise you could end up with an Anemic Data Model.
  • Herberto Graca thinks of these as objects that encapsulate business rules and state.
  • To create one of these in your app, fetch it using a repository.
  • It is not a necessity to have a new class here every time you implement a new use case. Perhaps there are no new business rules to be encapsulated in a domain entity class.
  • Do not allow this layer access to repositories. Injecting a repository into this layer will break the SRP. Only Application Layer should be able to access repositories.
  • Do not inject Application layer services into your domain layer entities.
  • Should you inject Domain layer services into your domain layer entities?
    • If a Domain service is needed in your domain layer, you should inject it exclusively as a method parameter. Do not hold a class reference to it, simply use it in that one method.
    •  In this answer, a solution is proposed that diverges from Graca’s answer on domain services: ProductIdGenerator is placed in domain services layer and fetches an identity from a backing store (most likely a database). This generator is abstracted away behind an interface: IProductIdGenerator. The generator is considered to belong to domain service layer and is injected into domain model via a method parameter. Please note the author emphasizes how domain mdoel entities are not allowed to have references to domain service layer objects, but can use them through method parameter injection. I am not sure injecting domain services to domain layer is good design – this way the domain layer is dependent on external implementations (even if those implementations are abstracted behind interfaces). I would rather do such orchestrating logic (like generating stuff) in the Application Layer and then pass the results to other domain services/layer objects. Bottom line: I want my domain layer to be independent as possible.
  • Events are raised on this layer.

Domain services layer

  • Business rules that don’t naturally map to any business entity. Think of these as a way to prevent business rules from spilling into AS layer.
  • Define repository interfaces in this layer. It is imperative for these interfaces to be defined here since they deal with domain models/aggregates/entities and as such are considered to be part of domain. Do not define these interface in Application Layer as they are not part of the application, but domain.
  • Herberto Graca thinks of these as objects that encapsulate business rules, but don’t have state.
  • Think of these as business domain models that do not get persisted nor do get fetched from some storage. Their job is to hold business rules. These could be:
    • Number generators.
    • Filtering logic, as per business rules.
  • To create one of these in your app, inject it into the application service. Don’t be afraid to use a factory if you want to use different domain services interchangeably.
  • It is not a necessity to have a new class here every time you implement a new use case. Perhaps there are no new business rules to be encapsulated in a domain service class.
  • As per Eric Evans:

    When a significant process or transformation in the domain is not a natural responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as standalone interface declared as a SERVICE. Define the interface in terms of the language of the model and make sure the operation name is part of the UBIQUITOUS LANGUAGE. Make the SERVICE stateless“.

Application services layer

  • Use case implementation. Orchestrates business entities and domain services in order to fulfill a certain use case, as requested by stakeholders.
  • Provides a hosting environment for the execution of domain logic.
  • Exposes your domain logic through an API. This API is an implementation of the Facade pattern – translation of external commands to the domain model by offering an easy-to-use interface that hides the complexities of orchestrating various domain models needed to execute a business capability.
  • Application services layer exposes an API to be used by other application service layers.
  • Lev Gorodinski has a nice write-up about application services.
  • Make sure that none of the statements in AS constitute business decisions. This guy has written about that specific issue. If there is some business rule leakage into AS layer, you have to think about a couple of things:
    • Is it a major business rule or a minor one? If it is only a small detail leaking out, it might not be worth creating a new domain service.
    • However, if this same rule has leaked in multiple places in AS layer, then you’ll definitely have to refactor and move the rules into a domain service.
  • It’s ok for AS to call domain layer entities and domain services in order to execute a use case. It is also ok for AS to have an if statement as long as the logic that determines the result is within the domain entity and/or domain service.
  • Try to keep cyclomatic complexity of AS low.
  • In order to get a reference to business entities and domain services, take a look at  respective sections (Domain layer, Domain services layer) on how to get them.
  • Part of this orchestration involves handling non-domain related activities: e.g. sending email notification, putting events into queues etc.
  • Herberto Graca thinks of these as objects that do not business rules and don’t have state.
  • Inject repositories to fetch data.
  • Every time you’re implementing a new use case, you’ll either create or expand on some class in this layer.
  • DTOs:
    • defined in this layer. DTOs are returned by methods in this layer. Please bear in mind: it is not mandatory to return DTOs – if you have a simple domain model, you can expose it directly.
    • Note: DTOs are to be used only when sending data across process boundaries. Do not use DTOs to communicate between layers within your application if you are in the same process.
      • E.g. DTOs cannot be created and used by your application layer to bundle domain model data when calling a repository, just for the sake of convenience.
      • E.g. DTOs can be created and used internally by your repository when it is requesting data from somewhere.
      • E.g. DTOs can be created and used by your application layer when it is getting ready to return them to the client.
    • If what the clients require is a composition of various domain models or aggregates, you can model an appropriate DTO type to fit their needs and then assemble the aggregates into it as necessary. A nice diagram:

application_service

UI sublayer

  • MVC controllers.
  • If you require a domain service or application service, inject them into the controller (preferably via an interface).

Infrastructure sublayer

Tests sublayer

 

Advertisements

One thought on “Onion layer responsibilities”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s