Domain language for classified ads

Our developers were discussing the flow of publishing classified ads. They went through the creation process and got to the point when the user clicks the Publish button. Together with the domain expert, they discovered that ads cannot be published immediately since an ad could contain some malicious content. They decided to put some sort of approval process, which should take place after the user clicks Publish and the ad actually becomes visible on the website.

Developers quickly decided to create a property for their ClassifiedAd domain class, called Status. It should be an enum, which indicates different stages of the review and publication process. It could also be reused later for statuses that are yet not known. Since they want to have behavior in their domain model, they added the UpdateStatus method to the class, which looks like this:

public class ClassifiedAd
{
private ClassifiedAdStatus _status;

public void UpdateStatus(ClassifiedAdStatus newStatus)
{
_status = newStatus;
DomainEvents.Publish(
new ClassifiedAdStatusUpdated(_id, newStatus));
}
}

Now that the method also publishes a domain event, other parts of the system can subscribe to this event and do some other important actions.

So, after the user clicks Publish, the following would happen:

ad.UpdateStatus(ClassifiedAdStatus.Published);

After the review is complete, the ad would be activated like this:

ad.UpdateStatus(ClassifiedAdStatus.Activated);

It might seem acceptable. At the end of the day, our ClassifiedAd class is basically a state machine, where instances of this class move from one status to another through the lifecycle of the ad. However, we already miss the intent. Our language becomes weird; instead of saying that we want to publish the ad, we update the status. Instead of activating the ad, we... update the status again!

Even if all seems to work after some more behavior is added to the system, code like this starts to appear:

public void UpdateStatus(ClassifiedAdStatus newStatus)
{
if (newStatus == ClassifiedAdStatus.Published
&& (string.IsNullOrEmpty(_title)|| _price == 0 ||
string.IsNullOrEmpty(_text))
throw new DomainException(
"Ad cannot be activated because some mandatory fields are
empty");

if (newStatus == ClassifiedAdStatus.Activated
&& _status == ClassifiedAdStatus.ViolationReported)
throw new DomainException("Reported ads cannot be activated);

if (newStatus == ClassifiedAdStatus.Deactivated
&& _status != ClassifiedAdStatus.ViolationReported)
throw new DomainException("Only a reported ad can be
deactivated);

_status = newStatus;
DomainEvents.Publish(new AdStatusUpdated(newStatus));
}

Clearly, this is not the code we expect to see in such simple method. It takes too much responsibility and the logical blocks in this method barely relate to each other. But things get worse when it gets to the domain event handling:

public void Handle(ClassifiedAdStatusUpdated @event)
{
// controlling the ad visibility based on it's reported status
if (@event.Status == ClassifiedAdStatus.ViolationReported
&& event.Status == ClassifiedAdStatus.MaliciousContentDetected)
CommandDispatcher.Send(new UpdateAdVisibility(@event.Id,
false));
}

The number of flow control operators is growing and most of the behavior is now being driven by status updates, something that in the beginning was considered to be small and concise operation on a single property of the domain object. The intent of this update operation is dissolved and each call needs to be carefully controlled for side effects. A risk to damage an existing behavior when adding new features is now very real.

Discussions with domain experts also lost some meaning. Instead of using phrases like if the malicious content is detected, we hide the ad and inform our moderation group, it becomes now and then we query all ads with the status equals MaliciousContentDetected and sue the notification service to deliver a message to all users that have moderation rights. The meaning of the language gets lost behind technical gibberish mixed with generalized words such as status and message.

The team decides to refactor the code and use proper domain language instead. So, this is what they came up with:

public class ClassifiedAd
{
private ClassifiedAdStatus _status;

public void Publish()
{
_status = ClassifiedAdStatus.Published;
DomainEvents.Publish(new ClassifiedAdPublished(_id));
}
}

Now we can also refactor the domain event handling to something like this:

public void Handle(ClassifiedAdPublished @event) =>CommandDispatcher
.Send(new ShowClassifiedAd(@event.Id));

Our small example also shows that the domain language cannot be built by making some sort of glossary with nouns. A misconception about collecting a lot of nouns in a large list and calling it a domain language, definitely exists. But this is not a happy path and usually, it leads to something called an anemic model, which is considered as an anti-pattern. Classes in anemic models only have properties and properties are always named by nouns. But no less important part of every domain is the behavior. Nouns express what the domain operates with but verbs describe what is being done. Without verbs, our domain tends to be a set of magic actions when properties change values without any particular reason. But our code above clearly expresses the domain behavior by introducing verbs as part of the domain language. These verbs are precise, express the intent, and describe actions. They are used both in imperative style for actions and in past tense when describing the history when we publish domain events from our code.

In the preceding example, we not only improved our code and brought better understanding to what it does and what concepts are being present there. We actually discovered some new terms and concepts that our domain model would benefit from. We can start using this terminology when talking to domain experts and see if they understand it. Sometimes they might give developers strange looks, trying to understand their excitement because they knew this new concept already, it is part of their language and it was just never expressed in conversations between business and development people. Such breakthroughs not only make the code better and closer to the actual business model but also improve communication between developers and domain experts.

By making implicit things explicit, not only we discover missing concepts in our code, but we also put them into our domain model. This part is essential because the language that is used across the whole range of models; business and mental models, conceptual and visual models, domain models in diagrams and in code. This pattern of using the same concept, and in general—the same language, across multiple levels of models in the system, is called ubiquitous language.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.137.117