MData – Using interfaces as domain model

In a conventional domain model, most domain logic resides in then entities themselves, or in a domain layer called services/repository. This brings some advantages (scalability, POCO). But it also has some liabilities. This is certai ly the case when the domain relies on services to execute business logic. In those scenarios business logic is seperated from the domain model in an almost untestable way. Imagine an example where after an item is sold. Stock should be updated, and deminished by the quantity sold. In a pure POCO world, this could look like this.

I am not saying this approach is absolutely wrong. In the .net community, I saw once too many that people have a very black/white vision on how a domain model should look. I personally think you can/have to adapt the technical implementation to the requirement of the project. Some small projects may justify to use a very simple ‘home-grown’ application framework, while bigger projects usually benefit more from the bigger, wide spread frameworks. This is ofcourse only true if you know there are alternatives. That said, let’s look how I composed the domain model in this experimental framework.

MData Domain Model Entities

In MData, all entities in the domain are defined as interfaces. This means MData uses the ‘POCO principle’ by default. The advantage here is that the implementation of the actual properties/logic can be different in different layers of the domain. This may sound pretty dramatic, but it doesn’t have to be. Imagine a WCF service layer that needs extra validation on entities that are created/modified, etc. This is an example of how a typical domain entity could look like.

1 2 3 4 5 6 7 8 9 10 11 12 13
[MData("Customer")]
public interface ICustomer
{
int Id { get; set; }
string Name { get; set; }
string Notes { get; set; }
bool IsActive { get; set; }
DateTime CreatedOn { get; set; }
DateTime ModifiedOn { get; set; }
 
void Active();
void DeActivate();
}
view raw ICustomer.cs hosted with ❤ by GitHub

Now I have some issues with this model. First of all it contains some properties/methods I would like to be abstracted away from the Customer entity. We could imagine that other entities in the domain would also want to implement IsActive, in combination with Activate() and DeActivate() methods. The most obvious solution would be to add this to a specialized base class, possibly derived from a generic entity base class. Something like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class ActivatableEntity : EntityBase
{
public bool IsActive { get; set; }
public void Active()
{
//...
}
 
public void DeActivate()
{
//...
}
}
view raw ActivatableEntity.cs hosted with ❤ by GitHub

All fine and dandy, but as we are working with interfaces only, this is not an option. Of course it’s to simple to say we just can’t use such a typical OO way of doing things. In this simple scenario the disadvantage of using OO might not be clear right away. But imagine we want to abstract the auditing part of the entity (CreatedOn, ModifiedOn) away too. It could be done in a similar way as for IsActive abstraction, but then we cannot easily combine different ‘patterns’ in one entity. It wouldn’t be easy to use the IsActive in one entity, and both IsActive and Audit in another entity.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
public class ConcreteEntityA : ActivatableEntity
{
//...
}
 
public class ConcreteEntityB : ActivatableEntity, AuditEntity //not possible!!!
{
//...
}
 
public class AuditEntity : EntityBase
{
DateTime CreatedOn { get; set; }
DateTime ModifiedOn { get; set; }
}
 
public class ActivatableEntity : EntityBase
{
public bool IsActive { get; set; }
public void Active()
{
//...
}
 
public void DeActivate()
{
//...
}
}
view raw SampleDomain.cs hosted with ❤ by GitHub

Using interface to enable multi-inheritance

As you can see, it’s not easy to create a plug and play kind of domain model. This is were MData tries to solve something. As you can see in the previous code sample, one way to solve this problem would be multi inheritance. Normal classes are not an option for multi-inheritance in C#, but interfaces are! By using interfaces for the domain model we get multi inheritance for free. We can solve our requirement like this.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
public interface IConcreteEntityA : IActivatableEntity
{
//...
}
 
public interface IConcreteEntityB : IActivatableEntity, IAuditEntity //not possible!!!
{
//...
}
 
public interface IAuditEntity
{
DateTime CreatedOn { get; set; }
DateTime ModifiedOn { get; set; }
}
 
public interface IActivatableEntity
{
bool IsActive { get; set; }
void Active();
void DeActivate();
}
view raw MdataDomainModel.cs hosted with ❤ by GitHub

Yay, all fine and dandy those interfaces, but how will we implement our logic in the logic methods (De)Activate, or how can we define calculated properties? Well, that’s were the concept of DomainLogic comes in. DomainLogic is the actual implementation of an MData entity. It will hold the implementation of the methods defined in the interfaces, and the ability to override method getters to enable readonly properties. This is the implementation of the IActivatable domain interface.

1 2 3 4 5 6 7 8 9 10 11 12
public class ActivatableLogic : LogicBase<IActivatableEntity>
{
public void Active()
{
CurrentInstance.IsActive = true;
}
 
public void DeActivate()
{
CurrentInstance.IsActive = false;
}
}
view raw ActivatableLogic.cs hosted with ❤ by GitHub

To mapping of interface methods to the logicbase methods is done by convention. This convention is really simple: use exactly the same method definition. Because this is not ‘refactor-safe’ (a change in the interface would not cause our logic class to break the build), I usually define the MData model like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
public class ActivatableLogic : LogicBase<IActivatableEntity>, IActivatableEntityLogic
{
public void Active()
{
CurrentInstance.IsActive = true;
}
 
public void DeActivate()
{
CurrentInstance.IsActive = false;
}
}
 
[MData]
public interface IActivatableEntity : IActivatableEntityLogic
{
bool IsActive { get; set; }
}
 
public interface IActivatableEntityLogic
{
void Active();
void DeActivate();
}

This is a compromis between ‘type-safety’ and fragmentation of the domain model.

In the next article we will dive deeper on how the link between the interface and the LogicBase class is done.

Share On

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">