C# 了解丰富的领域模型和依赖关系
我试图了解丰富的域模型,以及如何将语义功能构建到域实体中,域实体与提供语义行为实现的对象之间没有紧密耦合 例如,我想在我的域模型中构建一个C# 了解丰富的领域模型和依赖关系,c#,asp.net-identity,domain-model,rich-domain-model,C#,Asp.net Identity,Domain Model,Rich Domain Model,我试图了解丰富的域模型,以及如何将语义功能构建到域实体中,域实体与提供语义行为实现的对象之间没有紧密耦合 例如,我想在我的域模型中构建一个用户实体,但我希望它的实现由identity框架驱动 class User { public string Email { get; set; } ... All of the other IdentityUser properties... public void DisableUser() { ...beh
用户
实体,但我希望它的实现由identity框架驱动
class User
{
public string Email { get; set; }
... All of the other IdentityUser properties...
public void DisableUser()
{
...behaviour to disable a user, most likely requires UserManager
}
public void AddToRole(Role role)
{
... most likely requires RoleManager
}
}
现在,我有了一个域模型,它根据业务规则运行,对持久性和实现一无所知
但是,如果DisableUser()
和AddToRole()
没有依赖关系,并且不以任何方式与UserManager
和rolemager
耦合,那么它们到底应该如何工作呢
- 一般来说,我缺少什么
- 域实体是否应该依赖于提供行为的对象
- 我应该如何将我的域模型与实现提供者分离
只读成员
这很容易,因为域充当其实体的工厂,因此每当它新建
其中一个实体时,它都将此
作为第一个构造函数参数传递。(实体应该具有程序集内部构造函数,因此除了域本身之外,任何人都不能实例化它们。)
如果你真的深入研究了ORM框架的文档,你通常会发现它们倾向于允许你为你的实体提供一个工厂,所以你可以这样做
因此,因为每个实体都有一个对域的引用,所以它可以从域中获得它所需要的任何工作。(想必,您的域对象将包含对UserManager
和rolemanger
,否?)这实际上是从依赖项注入中后退了一步:您将域对象与其依赖项一起注入,但您让域的每个实体从域对象获取其依赖项
以下是java中的一个示例:
package ...
import ...
public final class StarWarsDomain extends Domain
{
private static final Schema SCHEMA = ...
public StarWarsDomain( LogicDomain logicDomain, S2Domain delegeeDomain )
{
super( logicDomain, SCHEMA, delegeeDomain ); //these get stored in final members of 'Domain'
}
public UnmodifiableEnumerable<Film> getAllFilms()
{
return getAllEntitys( Film.COLONNADE ); //method of 'Domain'
}
public Film newFilm( String name )
{
assert !StringHelpers.isNullOrEmptyOrWhitespace( name );
Film film = addEntity( Film.COLONNADE ); //method of 'Domain'
film.setName( name );
return film;
}
}
包。。。
导入。。。
公共最终类StarWarsDomain扩展域
{
私有静态最终架构=。。。
公共StarWarsDomain(LogicDomain LogicDomain、S2Domain DelegeDomain)
{
super(logicDomain、SCHEMA、DelegeDomain);//这些存储在“域”的最终成员中
}
公共不可修改可枚举getAllFilms()
{
返回getAllEntitys(Film.COLONNADE);//域的方法
}
公共电影newFilm(字符串名称)
{
assert!StringHelpers.isNullOrEmptyOrWhitespace(名称);
Film-Film=addEntity(Film.COLONNADE);//域的方法
电影集名(名称);
回流膜;
}
}
精心设计的域模型不应依赖于任何其他体系结构层或服务。关于这一点,域模型对象应该是(在我的例子中)POCOs(普通的旧CLR对象)。服务和层(如业务逻辑或持久性层)应该依赖于这些对象并返回它们的实例
构建一个低耦合、高内聚和持久性的领域模型有几个关键点。在一句话中,其中的秘诀是“编写你希望拥有的代码”
域模型示例
public class Student
{
// Collections should be encapsulated!
private readonly ICollection<Course> courses;
// Expose constructors that express how students can be created.
// Notice that this constructor calls the default constructor in order to initialize the courses collection.
public Student(string firstName, string lastName, int studentNumber) : this()
{
FirstName = firstName;
LastName = lastName;
StudentNumber = studentNumber;
}
// Don't allow this constructor to be called from code.
// Your persistence layer should however be able to call this via reflection.
private Student()
{
courses = new List<Course>();
}
// This will be used as a primary key.
// We should therefore not have the ability to change this value.
// Leave that responsibility to the persistence layer.
public int Id { get; private set; }
// It's likely that students names or numbers won't change,
// so set these values in the constructor, and let the persistence
// layer populate these fields from the database.
public string FirstName { get; private set; }
public string LastName {get; private set; }
public int StudentNumber { get; private set; }
// Only expose courses via something that is read-only and can only be iterated over.
// You don't want someone overwriting your entire collection.
// You don't want someone clearing, adding or removing things from your collection.
public IEnumerable<Course> Courses => courses;
// Define methods that describe semantic behaviour for what a student can do.
public void Subscribe(Course course)
{
if(courses.Contains(course))
{
throw new Exception("Student is already subscribed to this course");
}
courses.Add(course);
}
public void Ubsubscribe(Course course)
{
courses.Remove(course);
}
}
公共班级学生
{
//集合应该被封装!
私人只读的ICollection课程;
//公开表示如何创建学生的构造函数。
//请注意,此构造函数调用默认构造函数以初始化courses集合。
公共学生(stringfirstname、stringlastname、int studentNumber):this()
{
名字=名字;
LastName=LastName;
StudentNumber=StudentNumber;
}
//不允许从代码调用此构造函数。
//然而,您的持久性层应该能够通过反射来调用它。
私立学生()
{
课程=新列表();
}
//这将用作主键。
//因此,我们不应该有能力改变这个值。
//将这一责任留给持久性层。
public int Id{get;private set;}
//学生的名字或数字很可能不会改变,
//因此,在构造函数中设置这些值,并让持久性
//图层从数据库填充这些字段。
公共字符串名{get;private set;}
公共字符串LastName{get;private set;}
public int StudentNumber{get;private set;}
//仅通过只读且只能迭代的内容公开课程。
//你不希望有人覆盖你的整个收藏。
//你不希望有人清理、添加或删除你收藏中的物品。
公共IEnumerable课程=>课程;
//定义描述学生语义行为的方法。
公众假期订阅(课程)
{
if(课程。包含(课程))
{
抛出新异常(“学生已订阅此课程”);
}
课程。添加(课程);
}
公开无效认购(课程)
{
课程。移除(课程);
}
}
诚然,这个领域模型对象是在考虑实体框架的情况下编写的,但它与通常的实体框架示例(相比之下,它们是贫乏的领域模型)相差甚远。在以这种方式构建域模型对象时,需要考虑一些注意事项,但Entity Framework会将它们持久化(有一点诡计),您会得到一个定义干净,语义收缩到依赖它的层。当我这样做时,我称我的富领域模型实体为主题,而面向领域编程的一般方法论是