Domain Context pattern

The problem
In your domain model, you often need to pass the same repetetive information to your domain entities or value objects. An example of this is a multi-tenant domain, where tenant ID appears in many places. A username might be needed if you need to track authors of changes to entities and it’s not a typical audit log scenario.

In such cases you quickly notice that many constructors and methods have the same parameters, like tenantId or userName. It would be beneficial to set it only once and access the values inside of your domain classes.

Possible solution
The very first thing that pops into the head is to use a static property (or apply Singleton pattern, but it’s discouraged anyway), something like this:

public class DomainWideValues
{
    public static TenantId TenantId { get; set; }
    public static string UserName { get; set; }
}

This is a very simple solution, but it wouldn’t work in ASP.NET applications properly. Different requests might be made by different tenants and users. Another problem might appear in parallel execution of unit tests – using the same static properties simultaneously means sharing state and that is a bad practice that leads to fragile tests.

Ambient Context pattern seems like a good solution to the problem. However, some modifications must be made.

Domain Context
NOTE: this is NOT related in any way to DomainContext class in WCF RIA services.

The solution that I’ve used many times is what I call a Domain Context. This is an ambient context variation that provides values for the domain model about the context that we are using it in. The implementation looks like this:

[Serializable]
public class DomainContext : IDisposable
{
	private static DomainContextStorage storage = new CallContextStorage();

	public static DomainContext Current
	{
		get { return Storage.Get(); }
	}

	public static DomainContextStorage Storage
	{
		get { return storage; }
		set { storage = value; }
	}

	public TenantId TenantId { get; private set; }
	public string UserName { get; private set; }

	public DomainContext(TenantId tenantId, string userName)
	{
		if(Storage.Get() != null)
			throw new InvalidOperationException("Only a single Domain Context can be created!");

		TenantId = tenantId;
		UserName = userName;

		Storage.Add(this);
	}

	public void Dispose()
	{
		Storage.Remove();
	}
}

It closely resembles the Ambient Context pattern implementation, but there are few things to note:

  • There is no support for sub-contexts. They are simply not needed for sharing values between domain objects.
  • DomainContextStorage class is used for storing context data. We are not tied to a single context per thread. This enables us to use this solution with any technology that we need: ASP.NET, WCF, Windows Forms, etc.

Usage
In it’s simplest form, Domain Context can be used like this:

using(new DomainContext(new TenantId("Gedgei Inc."), "Gediminas"))
{
	var someEntity = new SomeEntity();
	Console.WriteLine(someEntity.TenantId);
	Console.WriteLine(someEntity.Author);
}

Domain classes accesses Domain Context directly through DomainContext.Current property:

public SomeEntity()
{
	this.TenantId = DomainContext.Current.TenantId;
	this.Author = new Author(DomainContext.Current.UserName);
}

This example works in simple scenarios, like single-threaded applications and unit tests. To use Domain Context in other environments, we must pick or create the correct DomainContextStorage implementation for that environment.

Domain Context storage
DomainContext class uses a DomainContextStorage subclass to save it’s state for later retrieval. The default storage is CallContextStorage, which means that context data is stored in memory and only available for the current call stack. It’s using ThreadStatic attribute under the hood, so every thread gets it’s own Domain Context. The default storage can be used in single threaded applications and unit tests:

[SetUp]
public void FixtureSetup()
{
	new DomainContext(new TenantId("test"), "testuser");
}

[TearDown]
public void FixtureTearDown()
{
	DomainContext.Current.Dispose();
}

For other environments, we need more complex implementations of the storage. Here’s a list of what possible implementations can use for different environments:

  • ASP.NET – HttpContext.Session. Sessions are per-user and Domain Context can be initialized on user login.
  • WCF – OperationContext. Domain Context can be initialized using IInstanceContextInitializer if per-call-instance mode is used.
  • Windows Forms and WPF – a static variable storage would probably suffice. Domain Context could be initialized on application startup or when user logs in.

Conclusion
I have successfully used Domain Context pattern in multiple projects before. It can simplify your Domain classes by providing common data globally without any impact on testability. However, since DomainContext class is so tightly coupled to the domain classes, it belongs to the domain model too, so be careful not to put any infrastructure concerns there.

Source code demonstrating basic usage of Domain Context pattern can be found on GitHub:
Domain Context Demo code

Advertisements

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s