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.
Many web applications I've contributed in (mostly ASP.NET), need to handle multiple and different user types.
Let's say you have a school portal, which both pupils and teachers use daily. On the front page of the application the users are met with almost the same GUI except some links to some tools only the teachers have access to. Let's say this is a messaging tool. A teacher can have different roles which defines who the teacher can send to.
For example:
A teacher with the role Publisher are allowed to send to everyone in the school.
A teacher with no extra roles are only allowed to send to everyone in his/her classes.
In the future, parents will also be able to access this portal and see detailed information about their children.
The problem I always get into is that my code is always getting cluttered with if-statements when handling different user types all over the application. Not only because of the different user types, but also by different business rules. I feel I can't find any way to properly handle different user types.
I guess the concept of roles in ASP.NET kind of solves this, but you would still end up with if-statements around the application.
My question is: Is there any best-practice on how to handle different users/user types in an application without infecting the code with if-statements?
You should split these responsibilities (like the sending capabilities of a teacher for example). You can do this by using the strategy pattern.
So a teacher has an extra property Publishing, which serves as an interface. The implementation can have multiple realizations (e.g. NoPublishing for a teacher who does not have publishing functionality, or DefaultPublishing). Each teacher can have its Publishing property set to either NoPublishing or DefaultPublishing. It can even be changed runtime if needed.
An example:
public class Teacher
{
public IPublishing Publishing { get; }
}
interface IPublishing
{
void Send();
}
public NoPublishing : IPublishing
{
public void Send()
{
// Implementatation
}
}
public PublishDefault : IPublishing
{
public void Send()
{
// Send a message the default way
}
}
Create a teacher:
var teacher = new Teacher();
Create a publisher strategy.
var defaultStrategy = new PublishDefault();
Connect them
teacher.Publishing = defaultStrategy;
Now you can send a message by:
teacher.Publishing.Send();
Depending on which Publishing strategy has been connected it will either send nothing or send something the default way.
You only need to instantiate each used Publishing strategy once and reuse it for each Teacher (or even other classes who needs to be able to send).
When you need other publish functionality, just add a new strategy (e.g. SmsPublishing, LetterPublishing etc).
You can even change the strategy on the fly if it is needed (by reassigning the Publishing property).
Why not implementing the interface not directly in Teacher?
Separation of Concerns principle: IPublish contains a specific and different responsibility.
Possibly IPublish contains functionality that can be used later in different classes or even other projects, so it is more reusable.
Testing is easier since IPublish does not need any knowledge about Teacher.
Possibility to realtime change the behavior of publishing in Teacher.
(note: I don't have a compiler here, so code is only for explanation purposes).
I am writing a piece of software in c# .net 4.0 and am running into a wall in making sure that the code-base is extensible, re-usable and flexible in a particular area.
We have data coming into it that needs to be broken down in discrete organizational units. These units will need to be changed, sorted, deleted, and added to as the company grows.
No matter how we slice the data structure we keep running into a boat-load of conditional statements (upwards of 100 or so to start) that we are trying to avoid, allowing us to modify the OUs easily.
We are hoping to find an object-oriented method that would allow us to route the object to different workflows based on properties of that object without having to add switch statements every time.
So, for example, let's say I have an object called "Order" come into the system. This object has 'orderItems' inside of it. Each of those different kinds of 'orderItems' would need to fire a different function in the code to be handled appropriately. Each 'orderItem' has a different workflow. The conditional looks basically like this -
if(order.orderitem == 'photo')
{do this}
else if(order.orderitem == 'canvas')
{do this}
edit: Trying to clarify.
I'm not sure your question is very well defined, you need a lot more specifics here - a sample piece of data, sample piece of code, what have you tried...
No matter how we slice the data structure we keep running into a boat-load of conditional statements (upwards of 100 or so to start) that we are trying to avoid
This usually means you're trying to encode data in your code - just add a data field (or a few).
Chances are your ifs are linked to each other, it's hard to come up with 100 independent ifs - that would imply you have 100 independent branches for 100 independent data conditions. I haven't encountered such a thing in my career that really would require hard-coding 100 ifs.
Worst case scenario you can make an additional data field contain a config file or even a script of your choice. Either case - your data is incomplete if you need 100 ifs
With the update you've put in your question here's one simple approach, kind of low tech. You can do better with dependency injection and some configuration but that can get excessive too, so be careful:
public class OrderHandler{
public static Dictionary<string,OrderHandler> Handlers = new Dictionary<string,OrderHandler>(){
{"photo", new PhotoHandler()},
{"canvas", new CanvasHandler()},
};
public virtual void Handle(Order order){
var handler = handlers[order.OrderType];
handler.Handle(order);
}
}
public class PhotoHandler: OrderHandler{...}
public class CanvasHandler: OrderHandler{...}
What you could do is called - "Message Based Routing" or "Message Content Based" Routing - depending on how you implement it.
In short, instead of using conditional statements in your business logic, you should implement organizational units to look for the messages they are interested in.
For example:
Say your organization has following departments - "Plant Products", "Paper Products", "Utilities". Say there is only one place where the orders come in - Ordering (module).
here is a sample incoming message.
Party:"ABC Cop"
Department: "Plant Product"
Qty: 50
Product: "Some plan"
Publish out a message with this information. In the module that processes orders for "Plant Products" configure it such that it listens to a message that has "Department = Plant Products". This way, you push the onus on the department modules instead of on the main ordering module.
You can do this using NServiceBus, BizTalk, or any other ESB you might already have.
This is how you do in BizTalk and this is how you can do in NServiceBus
Have you considered sub-typing OrderItem?
public class PhotoOrderItem : OrderItem {}
public class CanvasOrderItem : OrderItem {}
Another option would be to use the Strategy pattern. Add an extra property to your OrderItem class definition for the OrderProcessStrategy and use a PhotoOrderStrategy/CanvasOrderStrategy to contain all of the different logic.
public class OrderItem{
public IOrderItemStrategy Strategy;
}
public interface IOrderItemStrategy{
public void Checkout();
public Control CheckoutStub{get;}
public bool PreCheckoutValidate();
}
public class PhotoOrderStrategy : IOrderItemStrategy{}
public class CanvasOrderStrategy : IOrderItemStrategy{}
Taking the specific example:
You could have some Evaluator that takes an order and iterates each line item. Instead of processing if logic raise events that carry in their event arguments the photo, canvas details.
Have a collection of objects 'Initiators' that define: 1)an handler that can process Evaluator messages, 2)a simple bool that can be set to indicate if they know what to do with something in the message, and 3)an Action or Process method which can perform or initiate the workflow. Design an interface to abstract these.
Issue the messages. Visit each Initiator, ask it if it can process the lineItem if it can tell it to do so. The processing is kicked off by the 'initiators' and they can call other workflows etc.
Name the pieces outlined above whatever best suits your domain. This should offer some flexibility. Problems may arise depending on concurrent processing requirements and workflow dependencies between the Initiators.
In general, without knowing a lot more detail, size of the project, workflows, use cases etc it is hard to comment.
I'm trying to use DDD when developing a new system. In this system I have places and I need to give access to places based on which adgroups you're a member of. I also need to get a list of allowed places from a list of adgroups.
I've come up with the following:
interface IPlaceRepository
{
Places[] GetPlacesForGroups(AdGroup[] adGroups);
}
class AdGroup()
{
string Name { get; private set; }
}
class Place
{
string Name { get; private set; }
}
Now I need to add a function that grants a group access to a particular place. According to DDD which is the right way to do it? I have a two suggestions.
I assume that adgroups can be considered as value objects.
Add a function to Place.
void GiveAccessTo(AdGroup adGroup) { ... }
and add a function to IPlaceRepository.
void AddGroupToPlace(Place p, Group g) { ... }
Then I need to inject the IPlaceRepository into Place for use inside GiveAccessTo.
Another way maybe is to create an ISecurityService? I can think of a method like this on that service.
void GiveAccessToPlace(AdGroup g, Place p)
In the same way as option 1 I need to implement a method on IPlaceRepository and inject the repository into the service.
Which is the DDD way to do this?
Repositories persist complete aggregates, normally you wouldn't have an AddGroupToPlace method.
Since ADGroup is a value object, you can use the GiveAccessTo method to add the groups to the Place aggregate. After doing that, you use the repository to persist the complete Place aggregate.
Services are mostly used when an operation spans multiple aggregates. This can often be avoided using events though.
(indirect answer)
Not sure if DDD has some rules specifically for your case. I would follow these steps:
Draw on a paper pad the aggregates, paying attention to the root aggregate (which entity contains another one)
Draw your queries
List several approaches to store the list of allowed items
Keep in mind how you would store this, in a document-oriented database or else where (this is where materialized view may complicate things)
Several approaches can be valid, return to your queries and pick the best approach (memory consumption, speed, least number of items required to be queried)
Separate security from business API, use Authorization pattern from the framework you use
Only use white-listing (list all allowed resources, deny all by default)
I am attempting to optimise around a possible bottleneck.
I have a server application that is serving objects from a database to applications remotely, who can work with 1 - n objects of 1 - n different types (where n can be a relatively high number) that all implement a common interface but may contain many unique properties on different types.
The client applications store the server objects in a local cache, until they are ready to persist them back, through the server, to the database.
This is being done currently in WCF with each class defining a DataContract.
Due to the possibly large amount of objects that may need to be passed back to the server (it changes depending on implementation), I would prefer not to do these all as individual calls any more, rather wrap all the objects in a single serialized (or better still compressed) stream and send them through as one connection to the server.
I can quite simply roll my own, but would prefer to use a recommended approach, and hoping someone may suggest one. If you can convince me, I am also willing to accept that my approach is probably not the best idea.
How high is "relatively high"?
For example, one option that occurs is to use a wrapper object:
[DataContract]
public class Wrapper {
[DataMember(Order = 1)]
public List<Foo> Foos {get {...}}
[DataMember(Order = 2)]
public List<Bar> Bars {get {...}}
[DataMember(Order = 3)]
public List<Blop> Blops {get {...}}
}
Then you should be able to send a single message with any number of Foo, Bar and/or Blop records. My inclusion of the Order attribute was deliberate - if you want to reduce the size of the stream, you might consider protobuf-net - with the above layout, protobuf-net can hook into WCF simply by including [ProtoBehavior] on the method (in the operation-contract interface) that you want to attack (at both client and server). This switches the transfer to use google's "protocol buffers" binary format, using base-64 to encode. If you are using the basic-http binding, this can also use MTOM if enabled, so even the base-64 isn't an issue. Using this, you can get significant data transfer savings (~1/5th the space based on the numbers shown).
(edit 1 - protobuf-net assumes that Foo, Bar and Blop also use the Order attribute)
(edit 2 - note that you could always break up the request into a number of mid-size Wrapper messages, then call a method to apply all the changes once you have them at the server (presumably in a staging table in the database))