Domain Driven Design with Web API revisited Part 2: the problem domain and DDD basics refreshed

Introduction

In the previous post we went through DDD at a very high level. We discussed the cases when DDD can be a good solution and when it could rather be overkill. We saw that a complex domain model with lots of behaviour and logic could well justify the usage of DDD. On the other hand a project with more simple use cases such as CRUD could be made lighter with just plain classes filled with public getters and setters. It’s also an option to just pick certain ideas from DDD in your project. DDD is full of useful guidelines and patterns that are beneficial to software projects of all sizes.

In this post we’ll describe the problem domain of our demo application. We’ll also refresh our knowledge of some basic DDD concepts.

The problem domain of the demo

As I mentioned in the previous post I selected a domain from my own work at my current work place: load testing. We offer different services to our customers and one of those is to test how much load a web site can take over a period of time. The load test is based on a set of steps in a load test programme called a load test scenario. It can include steps like the following:

  1. Go to http://www.mysite.com
  2. Log on with “username” and “password”
  3. Search for the key terms “DDD” and “.NET”
  4. Select the first product on the search result list
  5. Proceed with the checkout
  6. Log off

A load test can have a large number of parameters:

  • The number of simulated users
  • The location(s) where these simulated users will be going through the load test steps
  • The test duration
  • The number of loops, i.e. how many times each simulated user will perform the load test steps
  • How quickly each simulated user enters the load test
  • …and much more

This is not a marketing presentation so I won’t continue with this list, you’ll get the idea what load testing is about.

The company has other domains such as web site monitoring, Big Data services, administration. You can consider the collection of all fields a business is doing the domain of that company. The full domain can then be broken down into a number of sub-domains such as load testing, monitoring, Big Data etc. It can happen that a small business only has a single sub-domain. However, most often you’ll find at least a couple of sub-domains that you might not have considered as sub-domains at all: customer and user administration, payment management, package tracking etc.

Coming back to the demo we’ll concentrate on load testing. I won’t of course present our full source code. Instead we’ll concentrate on a small subset of the load testing domain with a limited amount of properties and behaviour so that we keep the model simple and manageable for a demo at this level. We’ll build up the code bit by bit and gradually add more stuff to it. We won’t see how to execute a load test, that’s way beyond the scope of this series. Instead we’ll concentrate on the insertion of a new load test to the complete load testing timetable. Each insertion will need to pass a couple of rules before it can be added to the timetable, e.g. there must be at least one load test agent.

We’ll fine-tune the exact terms of the domain when we start building the model. Before we do that let’s refresh our memories about some basic DDD concepts.

Entities

One group of domain objects are called entities. Let’s say what Eric Evans says about entities in his book:

Many objects are not fundamentally defined by their attributes, but by a thread of continuity and identity.

An entity is therefore an object with a unique ID. This unique ID is the most important property of an entity: it helps distinguish between two otherwise identical objects. We can have two people with the same name but if their IDs are different then we’re talking about two different people. Also, a person can change his/her name, but if the ID is the same then we know we’re talking about the same person. An ID is typically some integer or a GUID or some other string value randomly generated based on some properties of the object. Once the entity has been persisted, its ID should never change otherwise we lose track of it.

E.g. in Sweden every legal person and company receives a personal registration number: the birth date in the format ‘YYMMDD-‘ followed by 4 digits. I don’t know how the 4 digits are generated but I know that my personal registration number is unique in Sweden. No other person can have the same ID. Once you are given this ID it cannot be changed. From the point of view of the Swedish authorities this ID is my most important “property”.

Entities should ideally remain very clean POCO objects without any trace of technology-specific code. You may be tempted to add technology specific elements such as MVC attributes in here, like [Required] or [StringLength(80)]. DON’T DO THAT. EVER! We’ll see later where they belong.

Object equality for entities is usually based on their IDs. If EntityA.Id == EntityB.Id then the two entities are equal.

Value objects

A value object on the other hand lacks any sort of ID:

Many objects have no conceptual identity. These objects describe some characteristic of a thing.

E.g. an office building may have 100 windows that look identical. Depending on your structure of the building you may not care which exact window is located where. Just grab whichever and mount it in the correct slot. In this case it’s futile to assign an ID to the windows, you don’t care which one is which.

Value objects are often used to group certain properties of an Entity. E.g. a Person entity can have an Address property which includes other properties such as Street and City. In your business an Address may well be a value object. In other cases, such as a postal company business, an Address will almost certainly be an Entity. A value object can absolutely have its own logic. In fact they are a good place to include logic for the entity which has them as one of its properties. Note that if you save value objects in the database it’s reasonable to assume that they will be assigned an automatic integer ID, but that’s an implementation detail that only concerns how our domain is represented in the data store, the domain layer doesn’t care, it may as well ignore the ID assigned by the data storage mechanism completely.

Sometimes it’s not easy to decide whether a specific object is an entity or a value object. If two objects have identical properties but we still need to distinguish between them then it’s most likely an entity with a unique ID. If two objects have identical properties and we don’t care which one is which: a value object. If the same object can be shared, e.g. the same Name can be attached to many Person objects: most likely a value object.

Value objects have a couple of other noteworthy properties we need to keep in mind:

  • Equality is based on the properties of the value object. E.g. if Name consists of FirstName and LastName then NameA == NameB if FirstNameA == FirstNameB and LastNameA == LastNameB
  • Value objects are immutable. You shouldn’t directly change any properties of an existing value object but rather create a copy of the original and change the property of the copy instead. The reason is that value objects can be shared among entities and you certainly not want to overwrite some property of a value object for ALL entities that reference it. Another reason is that value objects are – as stated above – made up by its property values and not by an entity field for tracking purposes. We’ll see later on how this can be achieved in code, it’s really simple

Aggregate roots

I’ve separated out aggregate roots into a post of its own, you can view it here. In short aggregate roots are groups of objects that “belong together”, such as a Car with all its parts as described in the post referenced previously.

If you buy a new car at a dealership then you’ll obviously will want to have all its parts: wheels, engine, windshields etc. This works from the point of view of the dealership as well: the dealer will refuse to sell a car with no engine or with only 2 wheels. The car aggregate is viewed as a single unit with the Car object as its aggregate root which consists of an engine, 4 wheels, a number of doors etc.

Aggregate roots can reference each other but normally they should not have direct access to the non-root properties of the aggregate. You can provide public methods in an aggregate root that internally may change one or more of its properties. That’s fine, as the public method internally will – or should – ensure that the aggregate object remains in a consistent state.

Coming back to the Car object, it would be unwise to provide direct accessors to its wheels: Car.FrontLeftWheel.Rotate(degrees=45). That would totally mess up the state of the car. An external object should not be able to modify the state of an aggregate so that it remains in an inconsistent state. Instead, you can have a public method called Turn which accepts e.g. a direction. The Turn method internally may as well set the angle of the front wheels in a consistent manner so that the car doesn’t break apart in the middle of the road. The car will be in a valid state after calling the Turn function.

Also, aggregate roots are important to handle as one unit for data persistence purposes. If a car object is deleted from the database then it should have a cascading effect on its constituents. We shouldn’t have “car-less” engines and wheels floating around in the data store. In reality those records may not just be deleted but instead put in an inactive state so that they can be re-used or sold separately as spare parts. However, the most important rule for aggregates is that the aggregate root and its non-root constituents “move together” when data store operations occur:

  • A new car will also necessitate 4 new wheels, a number of doors etc. in the data store
  • If a car is deleted then its constituent non-root objects should also be deleted
  • If a car is updated by some method, e.g. ChangeFrontWheels, then the Car object is re-validated as a whole before updating it in the data store

Keep in mind that it’s perfectly reasonable to have an aggregate with a single object which will become its aggregate root. You don’t have to add at least one more object to the aggregate just to have “more than one”.

That’s enough for now. We’ll continue exploring DDD in the next post.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

5 Responses to Domain Driven Design with Web API revisited Part 2: the problem domain and DDD basics refreshed

  1. Pingback: Architecture and patterns | Michael's Excerpts

  2. Emerson says:

    I like the concept of Aggregate roots, but what is the difference between it and a good object oriented programming?

    • Andras Nemes says:

      A DDD project cannot be written without object oriented programming. Aggregate roots and OOP are not mutually exclusive concepts. Aggregate roots are a way to indicate that a group of objects “move together” as far as data access is concerned.
      //Andras

  3. César Castro says:

    Hi Andras, great article, I’m César Castro, please see my latest project in .dotnet core preview 2 called DDD Anemic Monolithic N-LayeredArchitecture with .Net Core Preview 2 https://github.com/cesarcastrocuba/nlayerappv3, its the newest version of N-LayeredArchitecture created by César de la Torre. If you want to collaborate/improve this project stay in touch with me in cesar_castro_cuba@msn.com.

  4. Hi Andras, great article, I’m César Castro, please see my latest project in .dotnet core preview 2 called DDD Anemic Monolithic N-LayeredArchitecture with .Net Core Preview 2 https://github.com/cesarcastrocuba/nlayerappv3, its the newest version of N-LayeredArchitecture created by César de la Torre. If you want to collaborate/improve this project stay in touch with me in cesar_castro_cuba@msn.com.

Leave a Reply

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

WordPress.com Logo

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

Facebook photo

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

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: