Related
I will use Airbnb as an example.
When you sign up an Airbnb account, you can become a host by creating a listing. To create a listing, Airbnb UI guides you through the process of creating a new listing in multiple steps:
It will also remember your furthest step you've been, so next time when you want to resume the process, it will redirect to where you left.
I've been struggling to decide whether I should put the listing as the aggregate root, and define methods as available steps, or treat each step as their own aggregate roots so that they're small?
Listing as Aggregate Root
public sealed class Listing : AggregateRoot
{
private List<Photo> _photos;
public Host Host { get; private set; }
public PropertyAddress PropertyAddress { get; private set; }
public Geolocation Geolocation { get; private set; }
public Pricing Pricing { get; private set; }
public IReadonlyList Photos => _photos.AsReadOnly();
public ListingStep LastStep { get; private set; }
public ListingStatus Status { get; private set; }
private Listing(Host host, PropertyAddress propertyAddress)
{
this.Host = host;
this.PropertyAddress = propertyAddress;
this.LastStep = ListingStep.GeolocationAdjustment;
this.Status = ListingStatus.Draft;
_photos = new List<Photo>();
}
public static Listing Create(Host host, PropertyAddress propertyAddress)
{
// validations
// ...
return new Listing(host, propertyAddress);
}
public void AdjustLocation(Geolocation newGeolocation)
{
// validations
// ...
if (this.Status != ListingStatus.Draft || this.LastStep < ListingStep.GeolocationAdjustment)
{
throw new InvalidOperationException();
}
this.Geolocation = newGeolocation;
}
...
}
Most of the complex classes in the aggregate root are just value objects, and ListingStatus is just a simple enum:
public enum ListingStatus : int
{
Draft = 1,
Published = 2,
Unlisted = 3,
Deleted = 4
}
But ListingStep could be an enumeration class that stores the next step the current step can advance:
using Ardalis.SmartEnum;
public abstract class ListingStep : SmartEnum<ListingStep>
{
public static readonly ListingStep GeolocationAdjustment = new GeolocationAdjustmentStep();
public static readonly ListingStep Amenities = new AmenitiesStep();
...
private ListingStep(string name, int value) : base(name, value) { }
public abstract ListingStep Next();
private sealed class GeolocationAdjustmentStep : ListingStep
{
public GeolocationAdjustmentStep() :base("Geolocation Adjustment", 1) { }
public override ListingStep Next()
{
return ListingStep.Amenities;
}
}
private sealed class AmenitiesStep : ListingStep
{
public AmenitiesStep () :base("Amenities", 2) { }
public override ListingStep Next()
{
return ListingStep.Photos;
}
}
...
}
The benefits of having everything in the listing aggregate root is that everything would be ensured to have transaction consistency. And the steps are defined as one of the domain concerns.
The drawback is that the aggregate root is huge. On each step, in order to call the listing actions, you have to load up the listing aggregate root, which contains everything.
To me, it sounds like except the geolocation adjustment might depend on the property address, other steps don't depend on each other. For example, the title and the description of the listing doesn't care what photos you upload.
So I was thinking whether I can treat each step as their own aggregate roots?
Each step as own Aggregate Root
public sealed class Listing : AggregateRoot
{
public Host Host { get; private set; }
public PropertyAddress PropertyAddress { get; private set; }
private Listing(Host host, PropertyAddress propertyAddress)
{
this.Host = host;
this.PropertyAddress = propertyAddress;
}
public static Listing Create(Host host, PropertyAddress propertyAddress)
{
// Validations
// ...
return new Listing(host, propertyAddress);
}
}
public sealed class ListingGeolocation : AggregateRoot
{
public Guid ListingId { get; private set; }
public Geolocation Geolocation { get; private set; }
private ListingGeolocation(Guid listingId, Geolocation geolocation)
{
this.ListingId = listingId;
this.Geolocation = geolocation;
}
public static ListingGeolocation Create(Guid listingId, Geolocation geolocation)
{
// Validations
// ...
return new ListingGeolocation(listingId, geolocation);
}
}
...
The benefits of having each step as own aggregate root is that it makes aggregate roots small (To some extends I even feel like they're too small!) so when they're persisted back to data storage, the performance should be quicker.
The drawback is that I lost the transactional consistency of the listing aggregate. For example, the listing geolocation aggregate only references the listing by the Id. I don't know if I should put a listing value object there instead so that I can more information useful in the context, like the last step, listing status, etc.
Close as Opinion-based?
I can't find any example online where it shows how to model this wizard-like style in DDD. Also most examples I've found about splitting a huge aggregate roots into multiple smaller ones are about one-to-many relationships, but my example here is mostly about one-to-one relationship (except photos probably).
I think my question would not be opinion-based, because
There are only finite ways to go about modeling aggregates in DDD
I've introduced a concrete business model airbnb, as an example.
I've listed 2 approaches I've been thinking.
You can suggest me which approach you would take and why, or other approaches different from the two I listed and the reasons.
Let's discuss a couple of reasons to split up a large-cluster aggregate:
Transactional issues in multi-user environments.
In our case, there's only one Host managing the Listing. Only reviews could be posted by other users. Modelling Review as a separate aggregate allows transactional consistency on the root Listing.
Performance and scalability.
As always, it depends on your specific use case and needs. Although, once the Listing has been created, you would usually query the entire listing in order to present it to the user (apart from perhaps a collapsed reviews section).
Now let's have a look at the candidates for value objects (requiring no identity):
Location
Amenities
Description and title
Settings
Availability
Price
Remember there are advantages to limiting internal parts as value objects. For one, it greatly reduces overall complexity.
As for the wizard part, the key take away is that the current step needs to be remembered:
..., so next time when you want to resume the process, it will redirect to where you left.
As aggregates are conceptually a unit of persistence, resuming where you left off will require us to persist partially hydrated aggregates. You could indeed store a ListingStep on the aggregate, but does that really make sense from a domain perspective? Do the Amenities need to be specified before the Description and Title? Is this really a concern for the Listing aggregate or can this perhaps be moved to a Service? When all Listings are created through the use of the same Service, this Service could easily determine where it left off last time.
Pulling this wizard approach into the domain model feels like a violation of the Separation of Concerns principle. The B&B domain experts might very well be indifferent concerning the wizard flow.
Taking all of the above into account, the Listing as aggregate root seems like a good place to start.
UPDATE
I thought about the wizard being the concept of the UI, rather than of the domain, because in theory, since each step doesn't depend on others, you can finish any step in any order.
Indeed, the steps being independent is a clear indication that there's no real invariant, posed by the aggregate, on the order the data is entered. In this case, it's not even a domain concern.
I have no problem modeling those steps as their own aggregate roots, and have the UI determine where it left off last time.
The wizard steps (pages) shouldn't map to aggregates of their own. Following DDD, user actions will typically be forwarded to an Application API/Service, which in turn can delegate work to domain objects and services. The Application Service is only concerned with technical/infrastructure stuff (eg persistence), where as the domain objects and services hold the rich domain logic and knowledge. This often referred to as the Onion or Hexagonal architecture. Note that the dependencies point inward, so the domain model depends on nothing else, and knows about nothing else.
Another way to think about wizards is that these are basically data collectors. Often at the last step some sort of processing is done, but all steps before that usually just collect data. You could use this feature to wrap all data when the user closes the wizard (prematurely), send it to the Application API and then hydrate the aggregate and persist it until next time the user comes round. That way you only need to perform basic validation on the pages, but no real domain logic is involved.
My only concern of that approach is that, when all the steps are filled in and the listing is ready to be reviewed and published, who's responsible for it? I thought about the listing aggregate, but it doesn't have all the information.
This is where the Application Service, as a delegator of work, comes into play. By itself it holds no real domain knowledge, but it "knows" all the players involved and can delegate work to them. It's not an unbound context (no pun intended), as you want to keep the transactional scope limited to one aggregate at a time. If not, you'll have to resort to two stage commits, but that's another story.
To wrap it up, you could store the ListingStatus on Listing and make the invariant behind it a responsibility of the root aggregate. As such, it should have all the information, or be provided with it, to update the ListingStatus accordingly. In other words, it's not about the wizard steps, it's about the nouns and verbs that describe the processes behind the aggregate. In this case, the invariant that guards all data is entered and that it is currently in a correct state to be published. From then on, it's illegal to return to, and persist, the aggregate with only partial state or in an incoherent manner.
Like any other aggregate. It shouldn't care if you collect the needed data in a multistep wizard or in just one screen. It's a UI issue, gathering the data and passing it to the domain at the end of the wizard.
You're trying to design your system based on the UI (the wizard step)!
In Domain-Driven Design you shouldn't really care about the UI (which is a technical detail),
you should look for the bounded contexts, invariants, etc.
For Example:
Listing bounded-context: property and guests, location, amenities, description and title
Booking bounded-context: booking settings, calendar and availability, pricing
Review bounded-context:
the listing doesn't have to be a global one,
you can display the listings for which you have all required information from the 'Listing context' and are availability for the search period, etc.
In my experience, DDD was a design methodology that came from a culture of what we'd now call Java backend data modeling. Modern web development has matured and evolved quite a bit since then with Angular/React/Vue frameworks that have their own paradigms about data modeling. Coming from a UX background, I'll elaborate on how to structure UI components that integrate with DDD models.
Separate data from presentation
MVC design works here. Naively, the end result of this workflow is the construction of a Listing domain model. But, I'm sure AirBnB's domain model for a listing is much more complex. Let's approximate that by considering each "step" as a form that constructs independent models. To simplify, let's only consider models for Photo and Location.
Class Photo: Class Location:
id guid
src geolocation
Provide a view for each model
Think of these UI components as "form" models that should work outside the context of a wizard. All of their fields are nullable, which represent incomplete steps. As an invariant, a view is valid iff it can construct a valid instance of the associated model.
Class PhotoView: Class LocationView:
id guid
src geolocation
valid { get } valid { get }
Define the Controller
Now, consider a View-Model WizardView to help orchestrate the independent views into "Wizard" behavior. We already have the independent views taking care of "valid/invalid" state. Now we just need an idea of "current" step. In the AirBnb UX, it seems like the "current" step is more of a "selected" state where the list item is expanded and all others are collapsed. Either way, a full page transition or "selected" represents the same state of "this step is active <-> all others are inactive." If _selected is null, traverse steps[] for the first invalid step, otherwise, null <--> all valid.
A StepView could display a whole page or, in the case of AirBnb, a single list item, where status == view.valid.
Class WizardView: Class StepView:
steps[] title
_selected view
selected { get set } status { get }
addStep(StepView)
submit()
The submit() represents whatever handling you want to trigger when all steps are valid and the domain models can be constructed. Notice how I've deferred the actual creation of any real domain model and only maintained "form" or "draft" data structures in the views. Only at the time of submit(), either on button press or as a callback to when the "all valid" event occurs, do these views bubble up data, most likely to make server request. You can construct a higher level Listing model here and make that your request payload. However, it is not the Wizard's job to communicate with the backend. It simply pools all the data together for a proper handler to construct a valid request.
Why? Ideally, the frontend should speak the same domain model that the backend does. At the very least your UX models should match one-to-one to high level aggregates. The idea for the frontend is to interface with a high-level layer of abstraction that the backend is not likely to change, while giving it the freedom to decompose and restructure that data in whatever internal domain it needs to. In practice, the frontend and backend domains get out of sync, so it's better leave a layer for data-munging at the request level so that the UX is internally consistent and coherent.
I'm being exposed to DDD for the first time here, mostly, it seems like good stuff but there are some things that are bothering me.
The main one at present is the idea that an object validates itself.
Previously, I would have written a service method similar to this:
public class MyService
{
private IValidator _validator;
private IDomainMapper _domainMapper;
private IThingThatDoesSomething _thingThatDoesSomething;
private IResponseMapper _responseMapper;
public MyService(IValidator validator, IDomainMapper domainMapper, IResponseMapper responseMapper)
{
_validator = validator;
_domainMapper = domainMapper;
_responseMapper = responseMapper;
}
public ResponseDTO DoSomething(RequestDto requestDto)
{
if (_validator.IsValid(requestDto))
{
var domainObject = _domainMapper.Map(requestDto);
var domainResponse = _thingThatDoesSomething.DoSomething(domainObject);
return _responseMapper.Map(domainResponse);
}
else
{
return new ResponseDTO { Valid = false, Errors = /* some error information */ };
}
}
}
However, thy colleagues who have spent more time that me studying DDD prefer the validation and mapping functionality to sit on the domain object. So the DTO look like:
public class RequestDto
{
public string Something {get; set; }
public DomainObject Map()
{
return new DomainObject { something = this.Something };
}
public bool IsValid()
{
return this.Something == "something valid";
}
}
This feel really wrong. Firstly, the object now has multiple responsibilities and secondly, from a domain driven perspective, it seems wrong as I wouldn't expect a letter to arrive at my desk which declares itself to be valid or not or know how to convert itself into something else.
Could someone explain why this is good in DDD?
First of all i think your original application service code looks better without applying your colleagues suggestions and this is not DDD related at all. Bare in mind that basic coding principles apply always, whether you're using DDD or writing a n-tier CRUD application. Specifically for your original code i mean:
You have a separate class for validation - this is good because you can reuse it.
You have a separate class for mapping from/to the data object - this is good as the domain object should not be bothered with any mapping details, especially when it is mapping to a data object.
On the other hand there are a few things which can be done better in terms of DDD:
Your mapper apparently can map in both ways (from domain object to data object and the other way around). In DDD domain objects are being created/materialised in a factory (no specific factory implementation is enforced) or/and in a repository. The important fact is that the domain object creation is the responsibility of domain itself, not the application service (as in your code).
You are validating the data object with your validator but i'm not sure you're doing the same input validation in your domain. In DDD many people take up an approach which can be summarized in a sentence: "Never let your domain get into an invalid state". I share that approach. In my projects i tend to have a separate validator for an entity if the validation logic is complex. This validator is used by the domain entity itself to validate the input parameters. For simple validation (eg. null and empty string checking) i leave it inside the domain object. As for your data object validation, most people (me included) tend to do it as "near" to the user interface as possible to get a better user experience (eg. faster response with validation errors).
One more thing worth mentioning is that, judging by your last code snippet, it think that you might've misunderstood your colleagues a bit at some point. Your RequestDTO class is not a domain object so even after getting some suggestions from your colleagues you shouldn't place the validation nor the mapping logic inside it. DTO is a data holder, nothing more.
TL;DR version
Mapping from a domain object to a data object is the responsibility of application layer (preffereably implemented in a separate mapper class).
Domain objects should not be mapped from data object by mappers in application layer. Creation of domain objects is the responsibility of domain itself (by means of factories).
Always validate data comming into your domain. Never let your domain get into an invalid state. Validation logic for entities should be placed inside the domain.
I've built an open source application, and I'd be curious to know how others are handling customer-specific requests. It's important to me to keep the app simple; I'm not trying to make it all things for all people. Apps can get bloated, complex, and just about unusable that way. However, there are some customer-specific options that would be nice (it just wouldn't apply to all customers). For example...
Say we have a domain entity called Server. In the UI, we let a customer pick from a list of servers. For one company, it's helpful to filter the servers by location (US, Germany, France, etc...). It would be easy enough to add a server property like this:
public class Server
{
public Location Location { get; set; }
// other properties here
}
My concern is that Server could become bloated with properties over time. And even if I only add location, not all customers would care about that property.
One option is to allow for user-defined fields:
public class Server
{
public string UserField1 { get; set; }
public string UserField2 { get; set; }
public string UserField3 { get; set; }
// etc...
// other properties here
}
Is that the best way to handle this? I don't like the fact that type safety is gone by making everything a string. Are there other/better ways that people are handling issues like this? Is there even a design pattern for something like this?
In my opinion, a good design pattern for something like this is to use schemas at the database level and then basic inheritance at the class level.
CREATE TABLE dbo.A (
ColumnA INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
ColumnB VARCHAR(50),
ColumnC INT,
etc.
)
And now we have a client who needs some specific functionality, so let's create an extension to this table in a different schema:
CREATE TABLE CustomerA.A (
ColumnA INT NOT NULL PRIMARY KEY,
Location VARCHAR(50)
)
But now we have another client who needs to extend it differently:
CREATE TABLE CustomerB.B (
ColumnA INT NOT NULL PRIMARY KEY,
DataCenterID INT
)
Though the fields may not be relevant, you get the idea, and so now we need to build the customer specific domain models here:
public abstract class A
{
public int ColumnA { get; set; }
public string ColumnB { get; set; }
public int ColumnC { get; set; }
}
public class CustomerA_A : A
{
public string Location { get; set; }
}
public class CustomerB_A : A
{
public int DataCenterID { get; set; }
}
And so now when we need to build something for Customer A, we'll build their subclass, and for Customer B theirs, and so on.
Now, FYI, this is the beginnings of a very dynamic system. I say that because the piece that's missing, that's not yet dynamic, is the user-interface. There is a significant number of ways that can be accomplished, but way outside the scope of this question. That is something you'll have to consider. I say that because the way you manage the interface will determine how you even know to build which subclass.
I hope this has helped.
The usual approach early on is to use the config XML files for this sort of thing. But programming for client-specific needs requires a whole mindset around how you program. Refer to this answer to a similar question.
Of course it always depends on how much customization you want to allow. In our product we went as far as enabling users to completely defined their own entities with properties and relations among them. Basically, every EntityObject, as we call our entities, in the end consists of a value collection and a reference to a meta-model describing the values within them. We designed our own query language that allows us to query the database and use expressions that are translate-able to any target language (although we currently only do SQL and .net).
The game does not end there and you quickly find that things like validation rules, permissions, default values and so on become a must have. Of course all of this then requires UI support, at least for the execution of the meta-model.
So it really depends on the amount of adjustment a end-user should be able to perform. I'd guess that in most cases simple user fields, as you described, will be sufficient. In that case I would provide a single field and store JSON text within that. In the UI you can then provide at least a semi-decent UI allowing structure and extensibility.
Option 1: Say "no". :-)
And while I say that (half) jokingly, there is some truth to it. Too often, developers open themselves up to endless customization by allowing one or two custom features, setting the snowball in motion.
Of course, this has to be balanced, and it sounds like you may be doing this to an extent. But if you truly want to keep your app simple, then keep it simple and avoid adding customizations like this.
Option 2: Inheritance.
If you really need to add the customization, I would lean the way of building a base class with all "standard" options, and then building customer-specific classes containing customer-specific optimizations.
For example:
public class Server
{
// all standard properties here
}
Then for Joe's Pizza, you can have:
public class JoesPizzaServer : Server
{
public Location Location { get; set; }
}
The side-benefit to this is that it will allow you to base your presentation views off of the client-specific (or base) models.
For example, in MVC you could set up your view models like this, and then you could have specific views for each customer.
For example, Bob's Burgers would have its own view on the base model:
#model MyApp.Server
#* implement the base form *#
And Joe's Pizza's view would use the custom model:
#model MyApp.JoesPizza
#* implement the base form -- a partial view -- with addtional custom fields
MVC does a really good job of supporting this type of pattern. If you're not using MVC (maybe WPF or Web Forms), there are still ways to leverage partial "view" files for accomplishing something similar.
Of course, your database can (and probably should) support a similar inheritance model. Entity Framework even supports various inheritance models like this.
I may be wrong here, but it looks like you want to handle different versions of your software with the same code base. I can think of two approaches for this:
Actually define different versions for it and handle changes for each client. This won't give you problems from the domain-modeling point of view, but will require a supporting infrastructure, which will have to scale according to your client requirements. There are some related questions out there (e.g. this, this and this).
Handle this at the domain-model level, as a user-defined configuration. The advantage of this approach is that you don't have to incorporate multiple versions of your software, but this comes at the expense of making your model more generic and potentially more complex. Also your tests will surely have to be adapted to handle different scenarios. If you are going in that direction I would model an object representing the attribute (with a name and a value) and consider the Server class as having a collection of attributes. In that way your model still captures your requirements in an OO style.
HTH
I approach from Python that I think would work rather well hear is a dictionary. The key is your field name, the value is the, errrrr... value ;)
It'd be simple enough to represent in a database too.
Danger ... Danger Dr. Smith... Philosophical post ahead
The purpose of this post is to determine if placing the validation logic outside of my domain entities (aggregate root actually) is actually granting me more flexibility or it's kamikaze code
Basically I want to know if there is a better way to validate my domain entities. This is how I am planning to do it but I would like your opinion
The first approach I considered was:
class Customer : EntityBase<Customer>
{
public void ChangeEmail(string email)
{
if(string.IsNullOrWhitespace(email)) throw new DomainException(“...”);
if(!email.IsEmail()) throw new DomainException();
if(email.Contains(“#mailinator.com”)) throw new DomainException();
}
}
I actually do not like this validation because even when I am encapsulating the validation logic in the correct entity, this is violating the Open/Close principle (Open for extension but Close for modification) and I have found that violating this principle, code maintenance becomes a real pain when the application grows up in complexity. Why? Because domain rules change more often than we would like to admit, and if the rules are hidden and embedded in an entity like this, they are hard to test, hard to read, hard to maintain but the real reason why I do not like this approach is: if the validation rules change, I have to come and edit my domain entity. This has been a really simple example but in RL the validation could be more complex
So following the philosophy of Udi Dahan, making roles explicit, and the recommendation from Eric Evans in the blue book, the next try was to implement the specification pattern, something like this
class EmailDomainIsAllowedSpecification : IDomainSpecification<Customer>
{
private INotAllowedEmailDomainsResolver invalidEmailDomainsResolver;
public bool IsSatisfiedBy(Customer customer)
{
return !this.invalidEmailDomainsResolver.GetInvalidEmailDomains().Contains(customer.Email);
}
}
But then I realize that in order to follow this approach I had to mutate my entities first in order to pass the value being valdiated, in this case the email, but mutating them would cause my domain events being fired which I wouldn’t like to happen until the new email is valid
So after considering these approaches, I came out with this one, since I am going to implement a CQRS architecture:
class EmailDomainIsAllowedValidator : IDomainInvariantValidator<Customer, ChangeEmailCommand>
{
public void IsValid(Customer entity, ChangeEmailCommand command)
{
if(!command.Email.HasValidDomain()) throw new DomainException(“...”);
}
}
Well that’s the main idea, the entity is passed to the validator in case we need some value from the entity to perform the validation, the command contains the data coming from the user and since the validators are considered injectable objects they could have external dependencies injected if the validation requires it.
Now the dilemma, I am happy with a design like this because my validation is encapsulated in individual objects which brings many advantages: easy unit test, easy to maintain, domain invariants are explicitly expressed using the Ubiquitous Language, easy to extend, validation logic is centralized and validators can be used together to enforce complex domain rules. And even when I know I am placing the validation of my entities outside of them (You could argue a code smell - Anemic Domain) but I think the trade-off is acceptable
But there is one thing that I have not figured out how to implement it in a clean way. How should I use this components...
Since they will be injected, they won’t fit naturally inside my domain entities, so basically I see two options:
Pass the validators to each method of my entity
Validate my objects externally (from the command handler)
I am not happy with the option 1 so I would explain how I would do it with the option 2
class ChangeEmailCommandHandler : ICommandHandler<ChangeEmailCommand>
{
// here I would get the validators required for this command injected
private IEnumerable<IDomainInvariantValidator> validators;
public void Execute(ChangeEmailCommand command)
{
using (var t = this.unitOfWork.BeginTransaction())
{
var customer = this.unitOfWork.Get<Customer>(command.CustomerId);
// here I would validate them, something like this
this.validators.ForEach(x =. x.IsValid(customer, command));
// here I know the command is valid
// the call to ChangeEmail will fire domain events as needed
customer.ChangeEmail(command.Email);
t.Commit();
}
}
}
Well this is it. Can you give me your thoughts about this or share your experiences with Domain entities validation
EDIT
I think it is not clear from my question, but the real problem is: Hiding the domain rules has serious implications in the future maintainability of the application, and also domain rules change often during the life-cycle of the app. Hence implementing them with this in mind would let us extend them easily. Now imagine in the future a rules engine is implemented, if the rules are encapsulated outside of the domain entities, this change would be easier to implement
I am aware that placing the validation outside of my entities breaks the encapsulation as #jgauffin mentioned in his answer, but I think that the benefits of placing the validation in individual objects is much more substantial than just keeping the encapsulation of an entity. Now I think the encapsulation makes more sense in a traditional n-tier architecture because the entities were used in several places of the domain layer, but in a CQRS architecture, when a command arrives, there will be a command handler accessing an aggregate root and performing operations against the aggregate root only creating a perfect window to place the validation.
I'd like to make a small comparison between the advantages to place validation inside an entity vs placing it in individual objects
Validation in Individual objects
Pro. Easy to write
Pro. Easy to test
Pro. It's explicitly expressed
Pro. It becomes part of the Domain design, expressed with the current Ubiquitous Language
Pro. Since it's now part of the design, it can be modeled using UML diagrams
Pro. Extremely easy to maintain
Pro. Makes my entities and the validation logic loosely coupled
Pro. Easy to extend
Pro. Following the SRP
Pro. Following the Open/Close principle
Pro. Not breaking the law of Demeter (mmm)?
Pro. I'is centralized
Pro. It could be reusable
Pro. If required, external dependencies can be easily injected
Pro. If using a plug-in model, new validators can be added just by dropping the new assemblies without the need to re-compile the whole application
Pro. Implementing a rules engine would be easier
Con. Breaking encapsulation
Con. If encapsulation is mandatory, we would have to pass the individual validators to the entity (aggregate) method
Validation encapsulated inside the entity
Pro. Encapsulated?
Pro. Reusable?
I would love to read your thoughts about this
I agree with a number of the concepts presented in other responses, but I put them together in my code.
First, I agree that using Value Objects for values that include behavior is a great way to encapsulate common business rules and an e-mail address is a perfect candidate. However, I tend to limit this to rules that are constant and will not change frequently. I'm sure you are looking for a more general approach and e-mail is just an example, so I won't focus on that one use-case.
The key to my approach is recognizing that validation serves different purposes at different locations in an application. Put simply, validate only what is required to ensure that the current operation can execute without unexpected/unintended results. That leads to the question what validation should occur where?
In your example, I would ask myself if the domain entity really cares that the e-mail address conforms to some pattern and other rules or do we simply care that 'email' cannot be null or blank when ChangeEmail is called? If the latter, than a simple check to ensure a value is present is all that is needed in the ChangeEmail method.
In CQRS, all changes that modify the state of the application occur as commands with the implementation in command handlers (as you've shown). I will typically place any 'hooks' into business rules, etc. that validate that the operation MAY be performed in the command handler. I actually follow your approach of injecting validators into the command handler which allows me to extend/replace the rule set without making changes to the handler. These 'dynamic' rules allow me to define the business rules, such as what constitutes a valid e-mail address, before I change the state of the entity - further ensuring it does not go into an invalid state. But 'invalidity' in this case is defined by the business logic and, as you pointed out, is highly volitile.
Having come up through the CSLA ranks, I found this change difficult to adopt because it does seem to break encapsulation. But, I agrue that encapsulation is not broken if you take a step back and ask what role validation truly serves in the model.
I've found these nuances to be very important in keeping my head clear on this subject. There is validation to prevent bad data (eg missing arguments, null values, empty strings, etc) that belongs in the method itself and there is validation to ensure the business rules are enforced. In the case of the former, if the Customer must have an e-mail address, then the only rule I need to be concerned about to prevent my domain object from becoming invalid is to ensure that an e-mail address has been provided to the ChangeEmail method. The other rules are higher level concerns regarding the validity of the value itself and really have no affect on the validity of the domain entity itself.
This has been the source of a lot of 'discussions' with fellow developers but when most take a broader view and investigate the role validation really serves, they tend to see the light.
Finally, there is also a place for UI validation (and by UI I mean whatever serves as the interface to the application be it a screen, service endpoint or whatever). I find it perfectly reasonably to duplicate some of the logic in the UI to provide better interactivity for the user. But it is because this validation serves that single purpose why I allow such duplication. However, using injected validator/specification objects promotes reuse in this way without the negative implications of having these rules defined in multiple locations.
Not sure if that helps or not...
I wouldn't suggest trowing big pieces of code into your domain for validation. We eliminated most of our awkward placed validations by seeing them as a smell of missing concepts in our domain. In your sample code you write I see validation for an e-mail address. A Customer doesn't have anything to do with email validation.
Why not make an ValueObject called Email that does this validation at construct?
My experience is that awkward placed validations are hints to missed concepts in your domain. You can catch them in Validator objects, but I prefer value object because you make the related concept part of your domain.
I am at the beginning of a project and I am going to implement my validation outside my domain entities. My domain entities will contain logic to protect any invariants (such as missing arguments, null values, empty strings, collections, etc). But the actual business rules will live in validator classes. I am of the mindset of #SonOfPirate...
I am using FluentValidation that will essentially give me bunch of validators that act on my domain entities: aka, the specification pattern. Also, in accordance with the patterns described in Eric's blue book, I can construct the validators with any data they may need to perform the validations (be it from the database or another repository or service). I would also have the option to inject any dependencies here too. I can also compose and reuse these validators (e.g. an address validator can be reused in both an Employee validator and Company validator). I have a Validator factory that acts as a "service locator":
public class ParticipantService : IParticipantService
{
public void Save(Participant participant)
{
IValidator<Participant> validator = _validatorFactory.GetValidator<Participant>();
var results = validator.Validate(participant);
//if the participant is valid, register the participant with the unit of work
if (results.IsValid)
{
if (participant.IsNew)
{
_unitOfWork.RegisterNew<Participant>(participant);
}
else if (participant.HasChanged)
{
_unitOfWork.RegisterDirty<Participant>(participant);
}
}
else
{
_unitOfWork.RollBack();
//do some thing here to indicate the errors:generate an exception (or fault) that contains the validation errors. Or return the results
}
}
}
And the validator would contain code, something like this:
public class ParticipantValidator : AbstractValidator<Participant>
{
public ParticipantValidator(DateTime today, int ageLimit, List<string> validCompanyCodes, /*any other stuff you need*/)
{...}
public void BuildRules()
{
RuleFor(participant => participant.DateOfBirth)
.NotNull()
.LessThan(m_today.AddYears(m_ageLimit*-1))
.WithMessage(string.Format("Participant must be older than {0} years of age.", m_ageLimit));
RuleFor(participant => participant.Address)
.NotNull()
.SetValidator(new AddressValidator());
RuleFor(participant => participant.Email)
.NotEmpty()
.EmailAddress();
...
}
}
We have to support more than one type of presentation: websites, winforms and bulk loading of data via services. Under pinning all these are a set of services that expose the functionality of the system in a single and consistent way. We do not use Entity Framework or ORM for reasons that I will not bore you with.
Here is why I like this approach:
The business rules that are contained in the validators are totally unit testable.
I can compose more complex rules from simpler rules
I can use the validators in more than one location in my system (we support websites and Winforms, and services that expose functionality), so if there is a slightly different rule required for a use case in a service that differs from the websites, then I can handle that.
All the vaildation is expressed in one location and I can choose how / where to inject and compose this.
You put validation in the wrong place.
You should use ValueObjects for such things.
Watch this presentation http://www.infoq.com/presentations/Value-Objects-Dan-Bergh-Johnsson
It will also teach you about Data as Centers of Gravity.
There also a sample of how to reuse data validation, like for example using static validation methods ala Email.IsValid(string)
I would not call a class which inherits from EntityBase my domain model since it couples it to your persistence layer. But that's just my opinion.
I would not move the email validation logic from the Customer to anything else to follow the Open/Closed principle. To me, following open/closed would mean that you have the following hierarchy:
public class User
{
// some basic validation
public virtual void ChangeEmail(string email);
}
public class Employee : User
{
// validates internal email
public override void ChangeEmail(string email);
}
public class Customer : User
{
// validate external email addresses.
public override void ChangeEmail(string email);
}
You suggestions moves the control from the domain model to an arbitrary class, hence breaking the encapsulation. I would rather refactor my class (Customer) to comply to the new business rules than doing that.
Use domain events to trigger other parts of the system to get a more loosely coupled architecture, but don't use commands/events to violate the encapsulation.
Exceptions
I just noticed that you throw DomainException. That's a way to generic exception. Why don't you use the argument exceptions or the FormatException? They describe the error much better. And don't forget to include context information helping you to prevent the exception in the future.
Update
Placing the logic outside the class is asking for trouble imho. How do you control which validation rule is used? One part of the code might use SomeVeryOldRule when validating while another using NewAndVeryStrictRule. It might not be on purpose, but it can and will happen when the code base grows.
It sounds like you have already decided to ignore one of the OOP fundamentals (encapsulation). Go ahead and use a generic / external validation framework, but don't say that I didn't warn you ;)
Update2
Thanks for your patience and your answers, and that's the reason why I posted this question, I feel the same an entity should be responsible to guarantee it's in a valid state (and I have done it in previous projects) but the benefits of placing it in individual objects is huge and like I posted there's even a way to use individual objects and keep the encapsulation but personally I am not so happy with design but on the other hand it is not out of the table, consider this ChangeEmail(IEnumerable> validators, string email) I have not thought in detail the imple. though
That allows the programmer to specify any rules, it may or may not be the currently correct business rules. The developer could just write
customer.ChangeEmail(new IValidator<Customer>[] { new NonValidatingRule<Customer>() }, "notAnEmail")
which accepts everything. And the rules have to be specified in every single place where ChangeEmail is being called.
If you want to use a rule engine, create a singleton proxy:
public class Validator
{
IValidatorEngine _engine;
public static void Assign(IValidatorEngine engine)
{
_engine = engine;
}
public static IValidatorEngine Current { get { return _engine; } }
}
.. and use it from within the domain model methods like
public class Customer
{
public void ChangeEmail(string email)
{
var rules = Validator.GetRulesFor<Customer>("ChangeEmail");
rules.Validate(email);
// valid
}
}
The problem with that solution is that it will become a maintenance nightmare since the rule dependencies are hidden. You can never tell if all rules have been specified and working unless you test every domain model method and each rule scenario for every method.
The solution is more flexible but will imho take a lot more time to implement than to refactor the method who's business rules got changed.
I cannot say what I did is the perfect thing to do for I am still struggling with this problem myself and fighting one fight at a time. But I have been doing so far the following thing :
I have basic classes for encapsulating validation :
public interface ISpecification<TEntity> where TEntity : class, IAggregate
{
bool IsSatisfiedBy(TEntity entity);
}
internal class AndSpecification<TEntity> : ISpecification<TEntity> where TEntity: class, IAggregate
{
private ISpecification<TEntity> Spec1;
private ISpecification<TEntity> Spec2;
internal AndSpecification(ISpecification<TEntity> s1, ISpecification<TEntity> s2)
{
Spec1 = s1;
Spec2 = s2;
}
public bool IsSatisfiedBy(TEntity candidate)
{
return Spec1.IsSatisfiedBy(candidate) && Spec2.IsSatisfiedBy(candidate);
}
}
internal class OrSpecification<TEntity> : ISpecification<TEntity> where TEntity : class, IAggregate
{
private ISpecification<TEntity> Spec1;
private ISpecification<TEntity> Spec2;
internal OrSpecification(ISpecification<TEntity> s1, ISpecification<TEntity> s2)
{
Spec1 = s1;
Spec2 = s2;
}
public bool IsSatisfiedBy(TEntity candidate)
{
return Spec1.IsSatisfiedBy(candidate) || Spec2.IsSatisfiedBy(candidate);
}
}
internal class NotSpecification<TEntity> : ISpecification<TEntity> where TEntity : class, IAggregate
{
private ISpecification<TEntity> Wrapped;
internal NotSpecification(ISpecification<TEntity> x)
{
Wrapped = x;
}
public bool IsSatisfiedBy(TEntity candidate)
{
return !Wrapped.IsSatisfiedBy(candidate);
}
}
public static class SpecsExtensionMethods
{
public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2) where TEntity : class, IAggregate
{
return new AndSpecification<TEntity>(s1, s2);
}
public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2) where TEntity : class, IAggregate
{
return new OrSpecification<TEntity>(s1, s2);
}
public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> s) where TEntity : class, IAggregate
{
return new NotSpecification<TEntity>(s);
}
}
and to use it, I do the following :
command handler :
public class MyCommandHandler : CommandHandler<MyCommand>
{
public override CommandValidation Execute(MyCommand cmd)
{
Contract.Requires<ArgumentNullException>(cmd != null);
var existingAR= Repository.GetById<MyAggregate>(cmd.Id);
if (existingIntervento.IsNull())
throw new HandlerForDomainEventNotFoundException();
existingIntervento.DoStuff(cmd.Id
, cmd.Date
...
);
Repository.Save(existingIntervento, cmd.GetCommitId());
return existingIntervento.CommandValidationMessages;
}
the aggregate :
public void DoStuff(Guid id, DateTime dateX,DateTime start, DateTime end, ...)
{
var is_date_valid = new Is_dateX_valid(dateX);
var has_start_date_greater_than_end_date = new Has_start_date_greater_than_end_date(start, end);
ISpecification<MyAggregate> specs = is_date_valid .And(has_start_date_greater_than_end_date );
if (specs.IsSatisfiedBy(this))
{
var evt = new AgregateStuffed()
{
Id = id
, DateX = dateX
, End = end
, Start = start
, ...
};
RaiseEvent(evt);
}
}
the specification is now embedded in these two classes :
public class Is_dateX_valid : ISpecification<MyAggregate>
{
private readonly DateTime _dateX;
public Is_data_consuntivazione_valid(DateTime dateX)
{
Contract.Requires<ArgumentNullException>(dateX== DateTime.MinValue);
_dateX= dateX;
}
public bool IsSatisfiedBy(MyAggregate i)
{
if (_dateX> DateTime.Now)
{
i.CommandValidationMessages.Add(new ValidationMessage("datex greater than now"));
return false;
}
return true;
}
}
public class Has_start_date_greater_than_end_date : ISpecification<MyAggregate>
{
private readonly DateTime _start;
private readonly DateTime _end;
public Has_start_date_greater_than_end_date(DateTime start, DateTime end)
{
Contract.Requires<ArgumentNullException>(start == DateTime.MinValue);
Contract.Requires<ArgumentNullException>(start == DateTime.MinValue);
_start = start;
_end = end;
}
public bool IsSatisfiedBy(MyAggregate i)
{
if (_start > _end)
{
i.CommandValidationMessages.Add(new ValidationMessage(start date greater then end date"));
return false;
}
return true;
}
}
This allows me to reuse some validations for different aggregate and it is easy to test. If you see any flows in it. I would be real happy to discuss it.
yours,
From my OO experience (I am not a DDD expert) moving your code from the entity to a higher abstraction level (into a command handler) will cause code duplication. This is because every time a command handler gets an email address, it has to instantiate email validation rules. This kind of code will rot after a while, and it will smell very badly. In the current example it might not, if you don't have another command which changes the email address, but in other situations it surely will...
If you don't want to move the rules back to a lower abstraction level, like the entity or an email value object, then I strongly suggest you to reduce the pain by grouping the rules. So in your email example the following 3 rules:
if(string.IsNullOrWhitespace(email)) throw new DomainException(“...”);
if(!email.IsEmail()) throw new DomainException();
if(email.Contains(“#mailinator.com”)) throw new DomainException();
can be part of an EmailValidationRule group which you can reuse easier.
From my point of view there is no explicit answer to the question where to put the validation logic. It can be part of every object depending on the abstraction level. In you current case the formal checking of the email address can be part of an EmailValueObject and the mailinator rule can be part of a higher abstraction level concept in which you state that your user cannot have an email address pointing on that domain. So for example if somebody wants to contact with your user without registration, then you can check her email against formal validation, but you don't have to check her email against the mailinator rule. And so on...
So I completely agree with #pjvds who claimed that this kind of awkward placed validation is a sign of a bad design. I don't think you will have any gain by breaking encapsulation, but it's your choice and it will be your pain.
The validation in your example is validation of a value object, not an entity (or aggregate root).
I would separate the validation into distinct areas.
Validate internal characteristics of the Email value object internally.
I adhere to the rule that aggregates should never be in an invalid state. I extend this principal to value objects where practical.
Use createNew() to instantiate an email from user input. This forces it to be valid according to your current rules (the "user#email.com" format, for example).
Use createExisting() to instantiate an email from persistent storage. This performs no validation, which is important - you don't want an exception to be thrown for a stored email that was valid yesterday but invalid today.
class Email
{
private String value_;
// Error codes
const Error E_LENGTH = "An email address must be at least 3 characters long.";
const Error E_FORMAT = "An email address must be in the 'user#email.com' format.";
// Private constructor, forcing the use of factory functions
private Email(String value)
{
this.value_ = value;
}
// Factory functions
static public Email createNew(String value)
{
validateLength(value, E_LENGTH);
validateFormat(value, E_FORMAT);
}
static public Email createExisting(String value)
{
return new Email(value);
}
// Static validation methods
static public void validateLength(String value, Error error = E_LENGTH)
{
if (value.length() < 3)
{
throw new DomainException(error);
}
}
static public void validateFormat(String value, Error error = E_FORMAT)
{
if (/* regular expression fails */)
{
throw new DomainException(error);
}
}
}
Validate "external" characteristics of the Email value object externally, e.g., in a service.
class EmailDnsValidator implements IEmailValidator
{
const E_MX_MISSING = "The domain of your email address does not have an MX record.";
private DnsProvider dnsProvider_;
EmailDnsValidator(DnsProvider dnsProvider)
{
dnsProvider_ = dnsProvider;
}
public void validate(String value, Error error = E_MX_MISSING)
{
if (!dnsProvider_.hasMxRecord(/* domain part of email address */))
{
throw new DomainException(error);
}
}
}
class EmailDomainBlacklistValidator implements IEmailValidator
{
const Error E_DOMAIN_FORBIDDEN = "The domain of your email address is blacklisted.";
public void validate(String value, Error error = E_DOMAIN_FORBIDDEN)
{
if (/* domain of value is on the blacklist */))
{
throw new DomainException(error);
}
}
}
Advantages:
Use of the createNew() and createExisting() factory functions allow control over internal validation.
It is possible to "opt out" of certain validation routines, e.g., skip the length check, using the validation methods directly.
It is also possible to "opt out" of external validation (DNS MX records and domain blacklisting). E.g., a project I worked on initially validated the existance of MX records for a domain, but eventually removed this because of the number of customers using "dynamic IP" type solutions.
It is easy to query your persistent store for email addresses that do not fit the current validation rules, but running a simple query and treating each email as "new" rather than "existing" - if an exception is thrown, there's a problem. From there you can issue, for example, a FlagCustomerAsHavingABadEmail command, using the exception error message as guidance for the user when they see the message.
Allowing the programmer to supply the error code provides flexibility. For example, when sending a UpdateEmailAddress command, the error of "Your email address must be at least 3 characters long" is self explanatory. However, when updating multiple email addresses (home and work), the above error message does not indicate WHICH email was wrong. Supplying the error code/message allows you to provide richer feedback to the end user.
I wrote a blog post on this topic a while back. The premise of the post was that there are different types of validation. I called them Superficial Validation and Domain Based Command Validation.
This simple version is this. Validating things like 'is it a number' or 'email address' are more often than not just superficial. These can be done before the command reaches the domain entities.
However, where the validation is more tied to the domain then it's right place is in the domain. For example, maybe you have some rules about the weight and type of cargo a certain lorry can take. This sounds much more like domain logic.
Then you have the hybrid types. Things like set based validation. These need to happen before the command is issued or injected into the domain (try to avoid that if at all possible - limiting dependencies is a good thing).
Anyway, you can read the full post here: How To Validate Commands in a CQRS Application
I'm still experimenting with this concept but you can try Decorators. If you use SimpleInjector you can easily inject your own validation classes that run ahead of your command handler. Then the command can assume it is valid if it got that far. However, This means all validation should be done on the command and not the entities. The entities won't go into an invalid state. But each command must implement its own validation fully so similar commands may have duplication of rules but you could either abstract common rules to share or treat different commands as truly separate.
You can use a message based solution with Domain Events as explained here.
Exceptions are not the right method for all validation errors, is not said that a not valid entity is an exceptional case.
If the validation is not trivial, the logic to validate the aggregate can be executed directly on the server and while you are trying to set new input you can raise a Domain Event to tell to the user (or the application that is using your domain) why the input is not correct.
I'm currently refactoring some code on a project that is wrapping up, and I ended up putting a lot of business logic into service classes rather than in the domain objects. At this point most of the domain objects are data containers only. I had decided to write most of the business logic in service objects, and refactor everything afterwards into better, more reuseable, and more readable shapes. That way I could decide what code should be placed into domain objects, and which code should be spun off into new objects of their own, and what code should be left in a service class. So I have some code:
public decimal CaculateBatchTotal(VendorApplicationBatch batch)
{
IList<VendorApplication> applications = AppRepo.GetByBatchId(batch.Id);
if (applications == null || applications.Count == 0)
throw new ArgumentException("There were no applications for this batch, that shouldn't be possible");
decimal total = 0m;
foreach (VendorApplication app in applications)
total += app.Amount;
return total;
}
This code seems like it would make a good addition to a domain object, because it's only input parameter is the domain object itself. Seems like a perfect candidate for some refactoring. But the only problem is that this object calls another object's repository. Which makes me want to leave it in the service class.
My questions are thus:
Where would you put this code?
Would you break this function up?
Where would someone who's following strict Domain-Driven design put it?
Why?
Thanks for your time.
Edit Note: Can't use an ORM on this one, so I can't use a lazy loading solution.
Edit Note2: I can't alter the constructor to take in parameters, because of how the would-be data layer instantiates the domain objects using reflection (not my idea).
Edit Note3: I don't believe that a batch object should be able to total just any list of applications, it seems like it should only be able to total applications that are in that particular batch. Otherwise, it makes more sense to me to leave the function in the service class.
You shouldn't even have access to the repositories from the domain object.
What you can do is either let the service give the domain object the appropriate info or have a delegate in the domain object which is set by a service or in the constructor.
public DomainObject(delegate getApplicationsByBatchID)
{
...
}
I'm no expert on DDD but I remember an article from the great Jeremy Miller that answered this very question for me. You would typically want logic related to your domain objects - inside those objects, but your service class would exec the methods that contain this logic. This helped me push domain specific logic into the entity classes, and keep my service classes less bulky (as I found myself putting to much logic inside the service classes like you mentioned)
Edit: Example
I use the enterprise library for simple validation, so in the entity class I will set an attribute like so:
[StringLengthValidator(1, 100)]
public string Username {
get { return mUsername; }
set { mUsername = value; }
}
The entity inherits from a base class that has the following "IsValid" method that will ensure each object meets the validation criteria
public bool IsValid()
{
mResults = new ValidationResults();
Validate(mResults);
return mResults.IsValid();
}
[SelfValidation()]
public virtual void Validate(ValidationResults results)
{
if (!object.ReferenceEquals(this.GetType(), typeof(BusinessBase<T>))) {
Validator validator = ValidationFactory.CreateValidator(this.GetType());
results.AddAllResults(validator.Validate(this));
}
//before we return the bool value, if we have any validation results map them into the
//broken rules property so the parent class can display them to the end user
if (!results.IsValid()) {
mBrokenRules = new List<BrokenRule>();
foreach (Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResult result in results) {
mRule = new BrokenRule();
mRule.Message = result.Message;
mRule.PropertyName = result.Key.ToString();
mBrokenRules.Add(mRule);
}
}
}
Next we need to execute this "IsValid" method in the service class save method, like so:
public void SaveUser(User UserObject)
{
if (UserObject.IsValid()) {
mRepository.SaveUser(UserObject);
}
}
A more complex example might be a bank account. The deposit logic will live inside the account object, but the service class will call this method.
Why not pass in an IList<VendorApplication> as the parameter instead of a VendorApplicationBatch? The calling code for this presumably would come from a service which would have access to the AppRepo. That way your repository access will be up where it belongs while your domain function can remain blissfully ignorant of where that data came from.
As I understand it (not enough info to know if this is the right design) VendorApplicationBatch should contain a lazy loaded IList inside the domain object, and the logic should stay in the domain.
For Example (air code):
public class VendorApplicationBatch {
private IList<VendorApplication> Applications {get; set;};
public decimal CaculateBatchTotal()
{
if (Applications == null || Applications.Count == 0)
throw new ArgumentException("There were no applications for this batch, that shouldn't be possible");
decimal Total = 0m;
foreach (VendorApplication App in Applications)
Total += App.Amount;
return Total;
}
}
This is easily done with an ORM like NHibernate and I think it would be the best solution.
It seems to me that your CalculateTotal is a service for collections of VendorApplication's, and that returning the collection of VendorApplication's for a Batch fits naturally as a property of the Batch class. So some other service/controller/whatever would retrieve the appropriate collection of VendorApplication's from a batch and pass them to the VendorApplicationTotalCalculator service (or something similar). But that may break some DDD aggregate root service rules or some such thing I'm ignorant of (DDD novice).