IMHO, one of the most brilliant DDD concept is the ability of separate contexts in a application. But I'm confused on how I can put everything to work together.
First, I know that one thing is not related to other. But my question falls exactly on the Infrastructure/ORM part.
For example, I have a domain object called Procedure (a medical procedure). In the Context of Registration, only thing that matters is Code and Name. But in Procedure Management Context, I have a lot of other fields like Price that belongs to this context specific.
How I can have two entities with same name (in different contexts) with different properties using EF Code First? Basically, I want to save all fields in the same table, but retrieve only 2 fields in one context, and all fields in other. How I can achieve this?
Relationships between bounded contexts pose the problem of how the development of one context influences the other over time. The safest way of dealing with related contexts is by creating an anticorruption layer (ACL).
Anything that shows domain concepts, relationships, rules, and so on. Since a bounded context is a boundary for a model, it could include concepts from multiple subdomains. Or a single subdomain could be modelled as multiple bounded contexts.
To identify bounded contexts, you can use a DDD pattern called the Context Mapping pattern. With Context Mapping, you identify the various contexts in the application and their boundaries. It's common to have a different context and boundary for each small subsystem, for instance.
Such a Bounded Context represents a boundary around a set of functional features (user stories / use cases). For example, everything that is related to customer management in an insurance scenario: create customer, update customer, update customer address, etc.
The following is inspired by the Shrink EF Models with DDD Bounded Contexts article.
Domain models:
namespace SO25454395.Domain.Registration
{
using System;
public class Procedure
{
public int Id { get; set; }
public Guid Code { get; set; }
public string Name { get; set; }
}
}
.
namespace SO25454395.Domain.ProcedureManagement
{
using System;
public class Procedure
{
public int Id { get; set; }
public Guid Code { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Bounded contexts:
namespace SO25454395.Infrastructure
{
using System.Data.Entity;
public class RegistrationContext : BaseContext<RegistrationContext>
{
public DbSet<Domain.Registration.Procedure> Prodedures { get; set; }
}
}
.
namespace SO25454395.Infrastructure
{
using System.Data.Entity;
public class ProcedureManagementContext : BaseContext<ProcedureManagementContext>
{
public DbSet<Domain.ProcedureManagement.Procedure> Procedures { get; set; }
}
}
The default Code First behavior is to create a new database for each context, so we disable this using a base class and specify the database that should be shared among all contexts.
namespace SO25454395.Infrastructure
{
using System.Data.Entity;
public class BaseContext<TContext> : DbContext where TContext : DbContext
{
static BaseContext()
{
Database.SetInitializer<TContext>(null);
}
protected BaseContext() : base("Database")
{
}
}
}
Finally we need a context containing all classes used to build the complete model. This context is used for initialization.
namespace SO25454395.Infrastructure
{
using SO25454395.Domain.ProcedureManagement;
using System.Data.Entity;
public class DatabaseContext : DbContext
{
public DbSet<Procedure> Procedures { get; set; }
public DatabaseContext() : base("Database")
{
}
}
}
Test:
namespace SO25454395.Tests
{
using SO25454395.Infrastructure;
using System;
using System.Linq;
using Xunit;
public class BoundedContextTests
{
[Fact]
public void Test()
{
using (var databaseContext = new DatabaseContext())
{
databaseContext.Database.Initialize(true);
}
var code = Guid.NewGuid();
var name = "Name";
var price = 123.45m;
using (var procedureManagementContext = new ProcedureManagementContext())
{
var procedure = new Domain.ProcedureManagement.Procedure { Code = code, Name = name, Price = price };
procedureManagementContext.Procedures.Add(procedure);
procedureManagementContext.SaveChanges();
}
using (var registrationContext = new RegistrationContext())
{
var procedure = registrationContext.Prodedures.Single(p => p.Code == code);
Assert.Equal(name, procedure.Name);
// procedure.Price is not available here.
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With