How does domain objects and services interact in DDD? - domain-driven-design

'So it was in this context we created a Order.adjust() method that delegated the call to OrderAdjust Service.
Having Order.adjust() has an advantage that it makes Order own the adjust operation.'
How is this done? Is the domain service injected?
$order = new Order();
$order->adjust(???);
How can the domain service do operations on domain entities when it's stateless?
If a domain service is injected into an entity, methods can only be called on the reference and thus state must exist?
$service = DomainService();
$entity = DomainEntity();
$entity->operation($service);
// Inside DomainEntity
public function operation(DomainService &$service)
{
// Operations are delegated to the domain service reference
$service->operation();
$service->operation2();
}
$another_entity = AnotherDomainEntity();
// What happened in the first object must be known here
// otherwise what's the point?
$another_entity->operation($service);
Shouldn't it be done like this or in an application service?
$domain_service = new DomainService();
$entity = new DomainEntity();
$another_entity = new AnotherDomainEntity();
$domain_service->performOperation($entity, $another_entity);
How are the operations between domain entities/objects done?
How do domain objects in general communicate? Where are they instantiated?
Code examples would be greatly appreciated.
Source:
http://stochastyk.blogspot.no/2008/05/domain-services-in-domain-driven-design.html

The question is similar to this one: https://softwareengineering.stackexchange.com/a/62193/19252.
The blog post you referenced does a good job on your question. To make it short: If it can be done (and unit-tested!) in a model, do it there. Domain services are rather exception than a rule.
Let me quote that post:
"- Are'nt Services bad and should'nt we use all objects as per OO?
Yes, Services tend to stand orthogonal to Object Oriented Design. [...] There is a huge tendency in the modelling world to use excessive number of services"
As for me, the tendency comes from flaws of .NET/Java persistence architectures, like impossibility to put business logic into setter methods.

Related

If the authentication involves some business logic, shouldn't it go to a Domain Service?

I'm reading "Implementing Domain Driven Design" by Vaughn Vernon and there is an excerpt that sounds quite wrong to me.
In the chapter related to Services, we are trying to model a business specific authentication process:
boolean authentic = false;
Tenant tenant = DomainRegistry
.tenantRepository()
.tenantOfId(aTenantId);
if (tenant != null && tenant.isActive()) {
User user = DomainRegistry
.userRepository()
.userWithUsername(aTenantId, aUsername);
if (user != null) {
authentic = tenant.authenticate(user, aPassword);
}
}
return authentic;
Immediately after we have:
Look at the additional burden that we've heaped on the client. It now
needs to understand much more about authentication than it should.
And a bit later we have:
The only business responsibility that the client should have is to coordinate the use of
a single domain-specific operation that handles all other details of
the business problem.
Following by:
UserDescriptor userDescriptor =
DomainRegistry
.authenticationService()
.authenticate(aTenantId, aUsername, aPassword);
And so far this makes completely sense to me.
Couple of pages later, referring to the same example, though, the book states:
[...] you may decide to place this somewhat technical implementation class in a location
outside the domain model. Technical implementation may be housed in a
Module in the Infrastructure Layer, for example.
And this makes also sense to me.
But, the following code is this:
package com.saasovation.identityaccess.infrastructure.services;
// ...
public class DefaultEncryptionAuthenticationService implements AuthenticationService {
// ...
#Override
public UserDescriptor authenticate(
TenantId aTenantId,
String aUsername,
String aPassword) {
// Guards here
UserDescriptor userDescriptor = null;
Tenant tenant = DomainRegistry
.tenantRepository()
.tenantOfId(aTenantId);
if (tenant != null && tenant.isActive()) {
String encryptedPassword =
DomainRegistry
.encryptionService()
.encryptedValue(aPassword);
User user =
DomainRegistry
.userRepository()
.userFromAuthenticCredentials(
aTenantId,
aUsername,
encryptedPassword);
if (user != null && user.isEnabled()) {
userDescriptor = user.userDescriptor();
}
}
return userDescriptor;
}
and this is what I don't understand. The Service, including the aforementioned business logic (plus some more details, that is, encryption) is placed in the Infrastructure Layer (see package name), which is a client of the Domain Model. This means that eventually the previous condition is not met:
Look at the additional burden that we've heaped on the client. It now
needs to understand much more about authentication than it should.
Shouldn't this Service be placed in the Domain layer? What makes it an Infrastructure Layer? Isn't the Infrastructure Layer considered a client for the Domain Layer, assuming we are using Hexagonal Architecture, as the book is actually doing?
Infrastructural components are not clients of the domain layer, they are implementations of domain abstractions.
By "client", the author of the book probably refers to whoever invokes the domain (business) operation.
Generally speaking, authentication can be implemented in various ways, so it makes sense to put one implementation or another in infrastructure layer; but authentication AS CONCEPT is considered to be a domain abstraction (e.g. interface), as expressed in the following line of code:
DomainRegistry.authenticationService()
Still, though, the example of DefaultEncryptionAuthenticationService is somewhat obscure to me. Package name indeed implies infrastructure, however implementation involves several abstracted services (tenant, user, encryption) and some business logic, which means we are actually dealing with a domain service.
(To better understand how abstractions and implementations play together, it is important to understand the dependency inversion principle).
I don't recall that specific book sample, but whether or not logic is considered domain logic depends on the context. For instance, encryption may be a domain concern in the context of a cryptography library, but an infrastructure concern in some other context.
When the context is not clearly defined then the line between domain & infrastructure become can be blurry.
If we look at the IDDD's repository, we can see that the AuthenticationService is considered a domain concern (like you'd expect) in the Identity & Access bounded context.

DDD - domain service to store entity in not primary infrastructure

I am thinking about scenario in a way of Domain Driven design, where I have entity, lets say Cv (Curriculum vitae), which state is saved in database via repository.
Now I need to store part of the Cv in another system (ElasticSearch), which is crucial for whole app functionality like searching.
How to handle it? I am thinking about these 2 options:
1. Use domain service IndexCvGatewayServiceInterface (as interfaces implemented in infrastructure)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, IndexCvGatewayServiceInterface $indexService)
{
$cvRepository->update($this);
$indexService->update($this);
}
}
2. Listen to domain event (create infrastructure listener)
class CvEntity
{
public function approve(CvRepositoryInterface $cvRepository, EventDispatcheInterface $dispatcher)
{
$cvRepository->update($this);
$dispatcher->dispatch(new CvApprovedEvent($this));
}
}
I like option 2. because it separates logic for non state change purposes into infrastructure, but there is also concern, that we should know about searching as important part of our app.
You're facing here Write and Read model. Ideally after persist your entity/aggregate in the write model you should dispatch the uncommitted events of this entity and listing/subscribe to them to generate the projections (partials in elastic in your use case). For reference: https://github.com/jorge07/symfony-5-es-cqrs-boilerplate/blob/symfony-5/src/Infrastructure/User/ReadModel/Projections/UserProjectionFactory.php#L17
IMO, Entity should not contain the repository.

Query remote rest service from ddd aggregate

I've read about the Double Dispatch pattern, which enables to pass service interfaces into aggregate methods: https://lostechies.com/jimmybogard/2010/03/30/strengthening-your-domain-the-double-dispatch-pattern/, http://blog.jonathanoliver.com/dddd-double-dispatch/.
In my domain I have a BitbucketIntegration aggregate, which is local copy of a remote bitbucket account with some additional domain specific data. Now, I have to synchronize repositories and teams, etc.. from the cloud to be able to do business operations on them. In my first implementation I was using a service to access the Bitbucket Cloud, then set the aggregate's repositories, teams, account. This way I had a DDD mixed with Anemic Domain Model, since half of the aggregates state was set using setter-like methods from the service. With Double Dispatch I can pass e.g. a BitbucketService interface into method arguments. This way, the aggregate can protect it's invariants more, since some of the data can only be verified by connecting to the rest service (e.g. if the aggregate's accessToken, bitbucketAccount and repositories are in sync), which was the service's responsibility. One more thing that smells is that I have an accessToken field in my aggregate, which is only a technical concern.
Are there any recommended patterns for keeping a copy of a remote resource in a ddd aggregate? Also, how to keep the technical side out of it? Or was the first method with a domain service good enough?
Now the code looks something like:
class BitbucketIntegration extends Aggregate<UUID> {
accountId: BitbucketId
repos: List<Repository>
localData: ...
// ... and more
Single integrateWith(accessToken, queryService) {
var id = queryService.getAccountAsync(accessToken);
var repos = queryService.getReposAsync(accessToken);
return Single.zip(id, repos,
(i, r) -> new BitbucketIntegratedEvent(accessToken, i, r))
.onSubscribe(event -> apply(event))
}
Observable doSomeBusinessLocally(data) { ... return events; }
// this is triggered by a saga
Single pollForChanges(queryService) {
var dataFromRemote = queryService.synchronizeAsync(this.accessToken);
....
return event;
}
}
class CommandHandler {
queryService: BitbucketService
Completable handle(integrateCmd) {
aggregate = repo.get(integrateCmd.id);
return aggregate.integrateWith(integrateCmd.accessToken, queryService)
.flatMap(event -> repo.store(event));
}
}
As a side note, I only query Bitbucket.
EDIT:
Martin Fowler writes about accessing an external system, including the definition of an Anti-Corruption Layer, which translates the remote resource representation to domain types.
If you inject infrastructure services into your Aggregate (by constructor or by method invocation) then you won't have a pure domain model anymore. This includes even services that have interfaces defined in the domain layer. It affects testability and introduces a dependency on the infrastructure. It also breaks the Single responsibility principle and it forces the Aggregate to know things it does not really need to.
The solution to this is to call the service before and pass the result to the Aggregate's method (i.e. in the Application layer).

Doubts on application structure and communication directions

I'm currently building a CQS-style DDD-application. I'm having some doubts on how all 'components' work with each other.
But first I'll give a brief overview about the application's structure:
ApplicationService
-> Receives command objects
-> doesn't return any results
-> Acts on Domain model
-> Speaks with Aggregate repository for domain modifications
QueryService
-> Bypasses domain model; doesn't speak with Aggregate Repositories
-> Executes queries against database to populate view
-> Returns 'Representation' objects
REST Controller
-> Receives HTTP requests and binds 'body content' & request params to Command objects
-> delegates to ApplicationService for POST, PUT & DELETE requests
-> Always returns at least some HTTP code
-> delegates to QueryService for GET requests
Infrastructure
-> Handles persistence to DB
-> Contains some scheduling operations
-> Handles foreign domain events our domain model is 'interested' in
'Open Host'
-> This is mainly a Facade to be used by other domains
-> Facade delegates methods to ApplicationService for domain modifications and to QueryService for data retrieval (bypassing Repositories)
My Questions:
Is it OK that a DomainEventHandler corresponds with a Repository and invokes some methods on a Aggregate? Or should it always correspond with an ApplicationService?
QueryService returns 'Representation' objects. These are used by UI AND by 'Open Host' Facade as return value. Is it OK these objects are reused as return value by Facade? Or should Facade create their own Objects, even the results are basically the same?
ApplicationService takes 'Commands' as input parameters. Is it OK these Commands are also used by the Open Host Facade? Or should the Facade only accept primitive values and convert them to Commands when delegating to ApplicationService?
DomainEventHandlers seem to reside on 'Infrastructure' layer. Is it possible that an ApplicationService or Domain Service also subscribes to an Domain Event? Or is this always an Infrastructure responsibility?
All advice is very welcome!
Is it OK that a DomainEventHandler corresponds with a Repository and invokes some methods on a Aggregate? Or should it always correspond with an ApplicationService?
In my experience, any handlers are application services.
QueryService returns 'Representation' objects. These are used by UI AND by 'Open Host' Facade as return value. Is it OK these objects are reused as return value by Facade? Or should Facade create their own Objects, even the results are basically the same?
There is a lot of discussion here about the differences between Open Host service and Application Service. It is not clear to me who would be using Open Host service, or why it exists.
ApplicationService takes 'Commands' as input parameters. Is it OK these Commands are also used by the Open Host Facade? Or should the Facade only accept primitive values and convert them to Commands when delegating to ApplicationService?
I would pass in primitives on the edges of the application and convert them into commands which are then handled in the Application Services
DomainEventHandlers seem to reside on 'Infrastructure' layer. Is it possible that an ApplicationService or Domain Service also subscribes to an Domain Event? Or is this always an Infrastructure responsibility?
I've always considered my handlers to be Application Services - things that are responsible for orchestrating a user case. So the use case might be "when EventX is received, send an email and update the database". In this example, you would probably consider "the code that sends the email" and "the code that saves to the database" to be infrastructure concerns, but the handler itself would not be.
public class ExampleHandler : IHandle<ExampleEvent>
{
private IRepository _repo;
private ISendEmails _emailer;
public ExampleHandler(Repository repo, ISendEmails emailer)
{
.... set the private fields..
}
public void When(ExampleEvent event)
{
_emailer.Send(event.whatever);
_repo.Save(something);
}
}
To be honest, I don't really think in terms of layers - i prefer a hexagonal architecture style of thinking. In the above example, the event handlers would just have dependencies injected into them and then go about their business.

Interface with service layer or domain objects themselves? (DDD)

I'm still learning about DDD and I have these two (probably simple) questions:
If a Factory creates new object/graph/aggregate instances, but also "reconstitutes" objects/graphs from the Repository, then:
(1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioural method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.
(2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?
You can see I'm all over the place. Please help.
(1) Does your service layer functions/jobs/tasks/unit-of-work call into the Factory or a behavioral method on the Entity instance or a DomainService function? I'm lost as to the call stack based on the responsibility of these components.
Usually - top level retrieves necessary aggregate root and calls a function on it. Sometimes top level retrieves multiple aggregate roots and pass them to domain service, but not often because domain service is a quite strong sign that there is unrecognized aggregate root. At the end - top level ensures aggregate root is persisted.
(2) Do Entity instances even have "behavioural methods" like above? For example does a Post have p.UpdatePost(string bodyText) or is that not a concern of the domain model and so the same should be achieved with the Repository? Or the service layer function, should it be calling the Repository in this case and the entity instance simply have behavioural methods that are specific to the domain and not persistence? But then, why does it sound like "updating a post" is a domain function when that's the user's goal?
Yes, they do. Domain model should be aware of it's state changes. And that's much more beneficial as it seems at first. Great thing about this is that You gain extensibility point. If client will walk week later to You and say that he wants system to check additional things when user updates post - instead of searching every line of post.bodyText="new value", You will be able to go straight to post.UpdatePost method and attach necessary things there.
On the other hand - CRUD is not mutually exclusive with domain driven design. E.g. - in my application, management of users and their roles is uninteresting enough that I'm not even trying to model it granularly. You need to recognize parts what matters in business Your application is describing and working with.
Keep in mind that domain driven design makes sense for complex applications only. Simple blog application doesn't need it.
(3) Am I wrong in assuming that a service layer (not Domain Services) should encapsulate how an interface interacts with the Domain Layer?
As I see it - application services are more for orchestrating infrastructure. If there is no infrastructure involved - then application service loses value:
Application services basically are just facades. And every facade is bad if complexity it adds overweights problems it solves.
Inside domain:
//aggregate root is persistence ignorant.
//it shouldn't reference repository directly
public class Customer{
public string Name {get; private set;}
public static Customer Register(string name){
return new Customer(name);
}
protected Customer(string name){
//here it's aware of state changes.
//aggregate root changes it's own state
//instead of having state changed from outside
//through public properties
this.Name=name;
}
}
//domain model contains abstraction of persistence
public interface ICustomerRepository{
void Save(Customer customer);
}
Outside of domain:
public class CustomerRepository:ICustomerRepository{
//here we actually save state of customer into database/cloud/xml/whatever
public void Save(Customer customer){
//note that we do not change state of customer, we just persist it here
_voodoo.StoreItSomehow(customer);
}
}
//asp.net mvc controller
public class CustomerController{
public CustomerController(ICustomerRepository repository){
if (repository==null)throw new ArgumentNullException();
_repository=repository;
}
public ActionResult Register(string name){
var customer=Customer.Register(name);
_repository.Save(customer);
}
}

Resources