After reading DDD - Modifications of child objects within aggregate and Update an entity inside an aggregate I still puzzled with the implementation of entity changes within an aggregate. For what I understand the aggregate root speaks for the whole (or entire aggregate) and delegates 'commands' changes down to the rest.
This last part, the delegating down to the rest is causing some problems. In the example below I want to change the quantity of a particular orderline. I'm addressing the root 'Order' and telling it to change the quantity of a orderline identified by a local identifier.
When all business rules are met an event can be created and applied on the aggregate. For now all the events are applied on the aggregate root, and I think that is a good practices, so all the commands are directed on the root and this changes the state of the aggregate. Also the aggregate root is the only one creating events, letting the world know what happened.
class Order extends AggregateRoot
{
private $orderLines = [];
public function changeOrderLineQuantity(string $id, int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->applyChange(new OrderLineQuantityChangedEvent(
$id, $quantity
));
}
private function onOrderLineQuantityChangedEvent(OrderLineQuantityChangedEvent $event)
{
$orderLine = $this->orderLines[$event->getId()];
$orderLine->changeQuantity($event->getQuantity());
}
}
class OrderLine extends Entity
{
private $quantity = 0;
public function changeQuantity(int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->quantity = $quantity;
}
}
But, when I am applying this implementation I have a problem, as you notice the business rule for checking the value of $quantity is located in two classes. This is on purpose, because I don't really know the best spot. The rule is only applied within the OrderLine class, thus it doesn't belong in Order. But when I'm removing this from Order events will be created that cannot be applied, because not all business rules are met. This is also something that is not wanted.
I can created a method in the class OrderLine like:
public function canChangeQuantity(int $quantity)
{
if ($quantity < 0) {
return false;
}
return true;
}
changing the method in the OrderLine to:
public function changeQuantity(int $quantity)
{
if ($this->canChangeQuantity($quantity) < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->quantity = $quantity;
}
Now I can alter the method within the Order class to:
public function changeOrderLineQuantity(string $id, int $quantity)
{
$orderLine = $this->orderLines[$event->getId()];
if ($orderLine->canChangeQuantity($quantity)) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->applyChange(new OrderLineQuantityChangedEvent(
$id, $quantity
));
}
Ensuring the business logic is where it belongs and also not in two places. This is an option, but if the complexity increases and the model becomes larger I can imagine that these practices become more complex.
For now I have to questions:
(1) How do you cope with alterations deep within the aggregate that are started from the root?
(2) When the business rules increase (e.g, max quantity is 10, but on Monday 3 more, and for product X max is 3 items). Is it good practices to supply each command / method on the aggregate root a domain services that is validating these business rules?
I have a problem, as you notice the business rule for checking the value of $quantity is located in two classes.
From an "object oriented" perspective, Order::changeOrderLineQuantity($id, $quantity) is a message. It is normal for messages to have schema, and for schema to restrict the range of values that are permitted in any given field.
So this code here:
public function changeOrderLineQuantity(string $id, int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
is an example of message validation, you are checking to see that quantity is in the allowed range of values because the general-purpose int primitive is too permissive.
What domain modelers using strongly typed languages will often do here is introduce a new type, aka a ValueObject, that models the data with its range restrictions.
// Disclaimer: PHP is not my first language
class Quantity {
public function __construct(int $quantity) {
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this.quantity = quantity
}
// ...
}
In the ease cases, Quantity, as understood by Orders::changeOrderLineQuantity(...) is the same domain concept as Quantity as understood by OrderLineQuantityChangedEvent(...) is the same domain concept as Quantity as understood by OrderLine::changeQuantity(...), and therefore you can re-use the same type everywhere; the type checker therefore ensures that the correct constraints are satisfied.
Edit
As noted by Eben Roux in the comments to this question, Quantity here should not be understood to be some universal, general-purpose type. It is instead specific to the context of Orders and OrderLines, and other parts of the code that share the same constraints for the same reason.
A complete solution might have several different Quantity types in different namespaces.
Related
For an attribute which need to be validated, lets say for an entity we have country field as VO
This country field needs to be validated to be alpha-3 code as per some business logic required by domain expert.
NOTE:
*We need to persist this country data as it can have other values also and possible in future there can be addition, updating and deleting of the country persisted data.
This is just one example using country code which may rarely change, there can be other fields which needs to be validated from persistence like validating some quantity with wrt data in persistence and it won't be efficient to store them in memory or prefetching them all.
Another valid example can be user creation with unique and valid domain email check, which will need uniqueness check from persistence
*
Case 1.
Doing validation in application layer:
If we call repository countryRepo.getCountryByCountryAlpha3Code() in application layer and then if the value is correct and valid part of system we can then pass the createValidEntity() and if not then can throw the error directly in application layer use-case.
Issue:
This validation will be repeated in multiple use-case if same validation need to be checked in other use-cases if its application layer concern
Here the business logic is now a part of application service layer
Case 2
Validating the country code in its value object class or domain service in Domain Layer
Doing this will keep business logic inside domain layer and also won't violate DRY principle.
import { ValueObject } from '#shared/core/domain/ValueObject';
import { Result } from '#shared/core/Result';
import { Utils } from '#shared/utils/Utils';
interface CountryAlpha3CodeProps {
value: string;
}
export class CountryAlpha3Code extends ValueObject<CountryAlpha3CodeProps> {
// Case Insensitive String. Only printable ASCII allowed. (Non-printable characters like: Carriage returns, Tabs, Line breaks, etc are not allowed)
get value(): string {
return this.props.value;
}
private constructor(props: CountryAlpha3CodeProps) {
super(props);
}
public static create(value: string): Result<CountryAlpha3Code> {
return Result.ok<CountryAlpha3Code>(new CountryAlpha3Code({ value: value }));
}
}
Is it good to call the repository from inside domain layer (Service
or VO (not recommended) ) then dependency flow will change?
If we trigger event how to make it synchronous?
What are some better ways to solve this?
export default class UseCaseClass implements IUseCaseInterface {
constructor(private readonly _repo: IRepo, private readonly countryCodeRepo: ICountryCodeRepo) {}
async execute(request: dto): Promise<dtoResponse> {
const someOtherKeyorError = KeyEntity.create(request.someOtherDtoKey);
const countryOrError = CountryAlpha3Code.create(request.country);
const dtoResult = Result.combine([
someOtherKeyorError, countryOrError
]);
if (dtoResult.isFailure) {
return left(Result.fail<void>(dtoResult.error)) as dtoResponse;
}
try {
// -> Here we are just calling the repo
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
const dataOrError = MyEntity.create({...request,
key: someOtherKeyorError.city.getValue(),
country: countryOrError.getValue(),
});
const commandResult = await this._repo.save(dataOrError.getValue());
return right(Result.ok<any>(commandResult));
} catch (err: any) {
return left(new AppError.UnexpectedError(err)) as dtoResponse;
}
}
}
In above application layer,
this part of code :
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
it it right to call the countryCodeRepo and fetch result or this part should be moved to domain service and then check the validity of the countryCode VO?
UPDATE:
After exploring I found this article by Vladimir Khorikov which seems close to what I was looking, he is following
As per his thoughts some domain logic leakage is fine, but I feel it will still keep the value object validation in invalid state if some other use case call without knowing that persistence check is necessary for that particular VO/entity creation.
I am still confused for the right approach
In my opinion, the conversion from String to ValueObject does not belong to the Business Logic at all. The Business Logic has a public contract that is invoked from the outside (API layer or presentation layer maybe). The contract should already expect Value Objects, not raw strings. Therefore, whoever is calling the business logic has to figure out how to obtain those Value Objects.
Regarding the implementation of the Country Code value object, I would question if it is really necessary to load the country codes from the database. The list of country codes very rarely changes. The way I've solved this in the past is simply hardcoding the list of country codes inside the value object itself.
Sample code in pseudo-C#, but you should get the point:
public class CountryCode : ValueObject
{
// Static definitions to be used in code like:
// var myCountry = CountryCode.France;
public static readonly CountryCode France = new CountryCode("FRA");
public static readonly CountryCode China = new CountryCode("CHN");
[...]
public static AllCountries = new [] {
France, China, ...
}
public string ThreeLetterCode { get; }
private CountryCode(string threeLetterCountryCode)
{
ThreeLetterCode = threeLetterCountryCode;
}
public static CountryCode Parse(string code)
{
[...] handle nulls, empties, etc
var exists = AllCountries.FirstOrDefault(c=>c.ThreeLetterCode==code);
if(exists == null)
// throw error
return exists;
}
}
Following this approach, you can make a very useful and developer-friendly CountryCode value object. In my actual solution, I had both the 2 and 3-letter codes and display names in English only for logging purposes (for presentation purposes, the presentation layer can look up the translation based on the code).
If loading the country codes from the DB is valuable for your scenario, it's still very likely that the list changes very rarely, so you could for example load a static list in the value object itself at application start up and then refresh it periodically if the application runs for very long.
I have the following value object code which validates CustCode by some expensive database operations.
public class CustCode : ValueObject<CustCode>
{
private CustCode(string code) { Value = code; }
public static Result<CustCode> Create(string code)
{
if (string.IsNullOrWhiteSpace(code))
return Result.Failure<CustCode>("Code should not be empty");
// validate if the value is still valid against the database. Expensive and slow
if (!ValidateDB(code)) // Or web api calls
return Result.Failure<CustCode>("Database validation failed.");
return Result.Success<CustCode>(new CustCode(code));
}
public string Value { get; }
// other methods omitted ...
}
public class MyEntity
{
CustCode CustCode { get; }
....
It works fine when there is only one or a few entity instances with the type. However, it becomes very slow for method like GetAll() which returns a lot of entities with the type.
public async IAsyncEnumerable<MyEntity> GetAll()
{
string line;
using var sr = File.OpenText(_config.FileName);
while ((line = await sr.ReadLineAsync()) != null)
{
yield return new MyEntity(CustCode.Create(line).Value); // CustCode.Create called many times
}
}
Since data in the file was already validated before saving so it's actually not necessary to be validated again. Should another Create function which doesn't validate the value to be created? What's the DDD idiomatically way to do this?
I generally attempt not to have the domain call out to retrieve any additional data. Everything the domain needs to do its job should be passed in.
Since value objects represent immutable state it stands to reason that once it has managed to be created the values are fine. To this end perhaps the initial database validation can be performed in the integration/application "layer" and then the CustCode is created using only the value(s) provided.
Just wanted to add an additional point to #Eben Roux answer:
In many cases the validation result from a database query is dependent on when you run the query.
For example when you want to check if a phone number exists or if some product is in stock. The answers to those querys can change any second, and though are not suited to allow or prevent the creation of a value object.
You may create a "valid" object, that is (unknowingly) becoming invalid in the very next second (or the other way around). So why bother running an expensive validation, if the validation result is not reliable.
I'm trying to apply ubiquitous language to my domain objects.
I want to convert a Data Transfer Object coming from a client into the domain object. The Aggregate's Constructor only accepts the required fields, and the rest of parameters should be passed using aggregate's API even when the Aggregate is being created(by say CreateAggregate command).
But the DTO to Aggregate mapping code becomes a bit messy:
if(DTO.RegistrantType == 0){
registrantType = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
registrantType = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
title,
weight,
registrantType,
route,
callNumber,
)
//look at this one:
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........
One thing I should mention is that this problem doesn't seem a domain specific problem.
How can I reduce these If-Else statements without letting my domain internals leakage, and with being sure that the aggregate(not a mapping tool) doesn't accept values that can invalide it's business rules, and with having the ubiquitous language applied?
Please don't tell me I can use AoutoMapper to do the trick. Please read the last part carefully.'
Thank you.
A typical answer would be to convert the DTO (which is effectively a message) into a Command, where the command has all of the arguments expressed as domain specific value types.
void doX(DTO dto) {
Command command = toCommand(dto)
doX(command)
}
void doX(Command command) {
// ...
aggregate.Route(command.connectionType)
}
It's fairly common for the toCommand logic use something like a Builder pattern to improve the readability of the code.
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
In cases like this one, the strategy pattern can help
ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)
Once that you recognize that ConnectionTypeFactory is a thing, you can think about building lookup tables to choose the right one.
Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */
ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
f = defaultConnectionFactory;
}
So why don't you use more inheritance
for example
class CompanyRegistration : Registration {
}
class PersonRegistraiton : Registration {
}
then you can use inheritance instead of your if/else scenario's
public class Aggregate {
public Aggregate (CompanyRegistration) {
registantType = RegistrantType.Company();
}
public Aggregate (PersonRegistration p) {
registrantType = RegistrantType.Person();
}
}
you can apply simmilar logic for say a setRoute method or any other large if/else situations.
Also, i know you don't want to hear it, you can write your own mapper (inside the aggegate) that maps and validates it's business logic
for example this idea comes from fluentmapper
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
.ThatSets(x => x.title).When(x => x != null).From(x => x.title)
It isn't too hard to write your own mapper that allow this kind of rules and validates your properties. And i think it will improve readability
In DDD, the domain model consists of entities and value objects, but what do we do when we need something in the model which is neither of these?
For example, I have introduced the following ScheduledItems<T> implementation in order to encapsulate scheduling specifics:
public class ScheduledItems<T>
{
private SortedDictionary<DateTime, T> scheduledItems;
public ScheduledItems()
{
scheduledItems = new SortedDictionary<DateTime, T>();
}
public void ScheduleItem(DateTime scheduledDate, T item)
{
scheduledItems.Add(scheduledDate, item);
}
public void RemoveItem(T item)
{
scheduledItems
.Where(x => x.Value.Equals(item))
.Select(x => x.Key)
.ToList()
.ForEach(k => scheduledItems.Remove(k));
}
}
This class will be used by a couple of entities for scheduling purposes.
At this point, this is neither an Entity (it has no identity) nor a Value Object (it is not immutable).
One solution is to turn it into a Value Object by making it immutable ('adding' or 'removing' items would return a new instance of ScheduledItems).
But is this really necessary for something which is not really associated to the domain? This class could be just like any other .NET collection.
That class looks like a repository for ScheduledItems. So ScheduledItem is the Entity and ScheduledItems is the the Repository with Add(), Remove() methods.
I guess it depends on why the items are sorted.
If they need to be sorted because of certain business rules then this should be part of your domain.
If they need to be sorted to be properly shown in the UI, then this most likely is just a bit of view logic that should not be part of the domain.
If none of the above, I would consider this a collection-like helper class that could be in a part of the infrastructure layer that could be used across the other layers.
I'd like to model an Address as a value object. As it is a good practice to make it immutable, I chose not to provide any setter, that might allow to modify it later.
A common approach is to pass the data to the constructor; however, when the value object is pretty big, that may become quite bloated:
class Address {
public function __construct(
Point $location,
$houseNumber,
$streetName,
$postcode,
$poBox,
$city,
$region,
$country) {
// ...
}
}
Another approach whould be to provide the arguments as an array, resulting in a clean constructor, but that might mess up the implementation of the constructor:
class Address {
public function __construct(array $parts) {
if (! isset($parts['location']) || ! $location instanceof Point) {
throw new Exception('The location is required');
}
$this->location = $location;
// ...
if (isset($parts['poBox'])) {
$this->poBox = $parts['poBox'];
}
// ...
}
}
That also looks a bit unnatural to me.
Any advice on how to correctly implement a pretty big value object?
The main issue with large list of parameters is readability and the danger that you will mix up parameters. You can tackle these issues with Builder pattern as described in Effective Java. It makes code more readable (especially languages that don't support named and optional parameters):
public class AddressBuilder {
private Point _point;
private String _houseNumber;
// other parameters
public AddressBuilder() {
}
public AddressBuilder WithPoint(Point point) {
_point = point;
return this;
}
public AddressBuilder WithHouseNumber(String houseNumber) {
_houseNumber = houseNumber;
return this;
}
public Address Build() {
return new Address(_point, _houseNumber, ...);
}
}
Address address = new AddressBuilder()
.WithHouseNumber("123")
.WithPoint(point)
.Build();
The advantages:
parameters are named so it is more readable
harder to mix up house number with region
can use your own order of parameters
optional parameters can be omitted
One disadvantage I can think of is that forgetting to specify one of the arguments (not calling WithHouseNumber for example) will result in a run time error, instead of compile time error when using constructor. You should also consider using more Value Objects like PostalCode for example (as oppose to passing a string).
On a related note, sometimes business requirements call for changing part of the Value Object. For example, when address was originally entered, the street number might have been misspelled and needs to be corrected now. Since you modeled Address as an immutable object there is not setter. One possible solution to this problem is to introduce a 'Side-Effect-Free function' on the Address Value Object. The function would return a copy of the object itself with the exception of a new street name:
public class Address {
private readonly String _streetName;
private readonly String _houseNumber;
...
public Address WithNewStreetName(String newStreetName) {
// enforce street name rules (not null, format etc)
return new Address(
newStreetName
// copy other members from this instance
_houseNumber);
}
...
}
This is a common problem with Domain Driven Design examples. The Domain Expert is missing and that is the person that would tell you what an Address is and its requirements. I would suspect that the Domain Expert would tell you that an Address does not have a Point. You might be a able to produce a Point from an Address but it wouldn't require a Point. Also a P.O. Box wouldn't be separate value in an Address. You might need a Post Office Box address class (POBoxAddress) I'm stating this because this class looks like it was defined by a developer not Shipping or Billing Domain Expert. By talking to the Domain Expert you can reduce your constructor parameter count.
2nd
You may start to group the parameters as Value Objects. You could create a City value object. That could require the City, Region/State and Country. I would think a City name doesn't mean much unless I know the Region and Country. Saying Paris means nothing but Paris, Illinois, US or Paris, Île-de-France, FR gives you a complete picture. So this would also reduce the count parameter count to the Address object.
If you go down DDD road find a Domain Expert for the Domain you are coding for, you should not be the expert. Sometimes problems should not be fixed by code or a nifty design pattern.
immutable is fit for concurrent compute, no Blocking and no Lock, immutable is for high performance and good scalability.
so Value Object can be running better in a concurrent system, include in distribute system, replace old VO with new VO, no need update, so no blocking.