Comments (5)
Similar to the questions asked here:
With an example of the red book by @VaughnVernon:
public Discussion startDiscussionFor(
ForumIdentityService aForumIdentityService,
Author anAuthor,
String aSubject,
String anExclusiveOwner) {
if (this.isClosed()) {
throw new IllegalStateException("Forum is closed.");
}
Discussion discussion =
new Discussion(
this.tenant(),
this.forumId(),
aForumIdentityService.nextDiscussionId(),
anAuthor,
aSubject,
anExclusiveOwner);
return discussion;
}
from php-ddd.
Nice topic... I have a few questions tho:
In a future solution, we would create a separate microservice and make them event-driven.
Why? Are they not part of the same bounded context?
`DeploymentRejected" event or similar.
It is not a constraint?
Regardless your answers, that's how I see your problem being solved so far:
<?php
namespace Webdevilopers\Employment
{
class EmploymentContract
{
private EmploymentPeriod $employmentPeriod;
public function deployTo(Workplace $workplace, DeploymentPeriod $deploymentPeriod) : Deployment
{
if (!$deploymentPeriod->isInside($this->employmentPeriod)) {
throw new \InvalidArgumentException('DeploymentPeriod must be inside the EmploymentPeriod.');
}
//... In case Deployment is part of the same context, just return
//return new Deployment($workplace, $deploymentPeriod);
//... In case Deployment is part or another context (Microservice) send and event
//$this->record(new DeploymentCreatedEvent())
}
}
class EmploymentPeriod{}
class Workplace{}
class DeploymentPeriod
{
public function isInside(EmploymentPeriod $employmentPeriod) : bool
{
return true; //or false
}
}
class Deployment
{
public function __construct(Workplace $workplace, DeploymentPeriod $deploymentPeriod) {}
}
class UseCase // Application
{
public function handleSameContext(DeployEmployeeCommand $command) : void
{
$contract = $this->contractRepository->get($command->contractId);
$workPlace = $this->workplaceRepository->get($command->workplaceId);
$deployment = $contract->deployTo($workPlace, new DeploymentPeriod($command->from, $command->to));
$this->deploymentRepository->save($deployment);
}
public function handleMicroservice(DeployEmployeeCommand $command) : void
{
$contract = $this->contractRepository->get($command->contractId);
$workPlace = $this->workplaceRepository->get($command->workplaceId);
$contract->deployTo($workPlace, new DeploymentPeriod($command->from, $command->to));
$this->contractRepository->save($contract);
}
}
}
from php-ddd.
Thank you so much for this detailled feedback @yourwebmaker. It almost feels like playing poker and you read my hand because the code suggestion is very close to the current solution.
Why? Are they not part of the same bounded context?
I guess this is an important point. And I think I often miss it. "Deployment" is a subdomain, just like "Contracting". But yes, I really think they belong together in a single bounded context.
return new Deployment($workplace, $deploymentPeriod);
Should become:
return new Deployment($contractId, $workplace, $deploymentPeriod);
I guess my greatest fear is that another developer could use this method to create an "invalid" Deployment
e.g. inside a migration console command. The invariants may be protected. But there is no guarantee that the period was checked against an EmploymentPeriod
. But does it matter really?
I often tend to force the developer pass an interface e.g. EmploymentContractReadModel
to the Deployment
constructor - then there is no way around it.
But it feels strange and maybe that is over-complication.
Another approach I'm playing with is making the Deployment
become a collection of value objects inside EmploymentContract
. There will only be few deployments per contract. No performance issues here.
But as mentioned this feels strange since I think they belond to different subdomains. On the other hand - remember all the "Order & order lines" examples out there... sigh
from php-ddd.
e.g. inside a migration console command
IMHO migrations should not be aware of domain objects. But this is totally out of the scope of this discussion.
If they belong on the same context, just use the returning/factory and persist it inside the command handler. Aggregates generating new aggregates is not an issue.
And keep in mind: All models are wrong, some are useful. Yours seem to be really useful for me.
I understood, it's testable, it's simple. So just go for it. In case it does not work later, refactor to something better. It's part of DDD process to try out models.
from php-ddd.
At the bottom line what we devs do is offering other devs a single-point-of-entrance via command handler and a factory (method) to create the aggregate.
But we don't have to ensure that he skips using our "guidance" and possible spanning business-rules by directly creating the aggregate?
We deliver "useful" tools and hope they use them correctly.
from php-ddd.
Related Issues (20)
- Repositories inside or outside Domain Services HOT 1
- Event Enriching and external changes to read-model data
- When, where and how to create Summary Events HOT 3
- Passing read models (value objects representing state) / domain service to aggregate methods HOT 6
- Unit testing value objects with internal datetime calculation HOT 14
- How to test application service command handlers dealing with read models? HOT 12
- Process Manager example with Symfony Messenger Command / Event Bus and ProophOS HOT 5
- Batch / Bulk operations handling multiple event-sourced aggregate roots HOT 3
- How to use factory methods on aggregates in CQRS - WRITE vs. READ model HOT 1
- How to keep read-models up-to-date when a name property was externally changed?
- How to upcast events with Prooph HOT 1
- Are CQRS commands part of the domain model? HOT 13
- Populate Projection with multiple tables HOT 2
- Where to call or pass a domain service? HOT 16
- How to implement the Equatable interface / Equals or SameValueAs method in value objects
- Domain Event Publisher for Doctrine Entities HOT 1
- Event Sourcing vs. Event-Driven Architecture (EDA)
- The repository pattern HOT 4
- Properties on Domain Events HOT 3
- PHP Command DTO with Symfony Constraints equivalent in Angular Forms HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from php-ddd.