了解富域模型和依赖项

时间:2016-10-14 19:08:37

标签: c# asp.net-identity domain-model rich-domain-model

我试图了解丰富的域模型以及如何在域实体中构建语义功能,其中域实体没有与提供语义行为实现的对象紧密耦合

例如,我想在我的域模型中构建一个User实体,但我希望它的实现由身份框架驱动

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()在没有依赖关系并且不以任何方式与UserManagerRoleManager耦合时应该如何工作?< / p>

  • 一般来说,我缺少什么?
  • 域实体是否应该依赖于提供行为的对象?
  • 我应该如何将我的域模型与实现提供程序分离?

2 个答案:

答案 0 :(得分:0)

我所做的是让每个富域模型实体都接收对中心域对象的引用作为构造函数参数,并将其存储为readonly成员。

这很容易,因为域充当其实体的工厂,因此只要它new中的一个,它就会传递this作为第一个构造函数参数。 (实体应该具有程序集内部构造函数,以便除了域本身之外的任何人都不能实例化它们。)

如果您真的深入了解ORM框架的文档,您通常会发现它们倾向于允许您为实体提供工厂,因此您可以执行类似的操作。

因此,由于每个实体都有对域的引用,因此它可以从中获取完成其工作所需的任何内容。 (据推测,你的域对象将包含对UserManagerRoleManager的引用,不是吗?)这实际上是从依赖注入中取出一个实用的步骤:你注入域对象及其依赖项,但是你让域的每个实体都从域对象中获取它的依赖关系。

这是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;
    }
}

答案 1 :(得分:0)

精心设计的域模型应该不依赖于任何其他体系结构层或服务。相对而言,域模型对象应该是(在我的情况下) POCO (普通旧CLR对象)。然后,业务逻辑或持久层等服务和层应依赖于这些对象并返回它们的实例。

建立一个尊重低耦合,高内聚和持久性无知的领域模型有几个关键。在一个声明中,其中的秘诀是&#34; 编写您希望拥有的代码&#34;。

域模型示例

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);
    }
}

当然,这个域模型对象是用Entity Framework编写的,但它与通常的实体框架示例(相比之下是贫血域模型)相差甚远。在以这种方式制作域模型对象时需要考虑一些注意事项,但实体框架将持久化它们(带有一点jiggery-pokery),并获得一个定义的域模型对象与依赖它的图层的清晰,语义合约。