域模型和相关数据(贫血域模型)

时间:2016-11-22 09:22:01

标签: c# architecture asp.net-core domain-driven-design entity-framework-core

我目前正在使用Entity Framework Core处理ASP .NET Core 1.0。我对数据库中的数据进行了一些复杂的计算,我不确定如何使用依赖注入构建一个合适的体系结构构建一个贫血域模型(http://www.martinfowler.com/bliki/AnemicDomainModel.html

(简体)示例:

我有以下实体:

EmployeeService

我想做一些复杂的计算来计算每月的TimeSheet。因为我无法访问Employee实体中的数据库,所以我在public class EmployeeService { private DbContext _db; public EmployeeService(DbContext db) { _db = db; } public List<CalculatedMonth> GetMonthlyTimeSheet(int employeeId) { var employee = _db.Employee.Include(x=>x.TimeEntry).ThenInclude(x=>x.Project).Single(); var result = new List<CalculatedMonth>(); //complex calculation using TimeEntries etc here return result; } } 中计算了TimeSheet。

GetMonthlyTimeSheet

如果我想获取TimeSheet,我需要注入EmployeeService并调用Employee

所以 - 我最终在服务中使用了很多GetThis()和GetThat()方法,尽管这些方法完全适合public class Employee { public int Id {get;set;} public string Name {get;set;} public List<TimeEntry> TimeEntries {get;set;} public List<CalculatedMonth> GetMonthlyTimeSheet() { var result = new List<CalculatedMonth>(); //complex calculation using TimeEntries etc here return result; } } public IActionResult GetTimeSheets(int employeeId) { var employee = _employeeRepository.Get(employeeId); return employee.GetTimeSheets(); } 类本身。

我想要达到的目标是:

public class Employee {
    public int Id {get;set;}
    public string Name {get;set;}
    public List<TimeEntry> TimeEntries {get;set;}

    public List<CalculatedMonth> GetMonthlyTimeSheet() { 
        if (TimeEntries == null)
          throw new PleaseIncludePropertyException(nameof(TimeEntries));

        var result = new List<CalculatedMonth>();

        //complex calculation using TimeEntries etc here

        return result;
    }
}

public class EmployeeService {
    private DbContext _db;
    public EmployeeService(DbContext db) {
        _db = db;
    }

    public Employee GetEmployeeWithoutData(int employeeId) {
        return _db.Employee.Single();
    }

    public Employee GetEmployeeWithData(int employeeId) {
        return _db.Employee.Include(x=>x.TimeEntry).ThenInclude(x=>x.Project).Single();
    }
}

public IActionResult GetTimeSheets(int employeeId) {
    var employee = _employeeService.GetEmployeeWithData(employeeId);
    return employee.GetTimeSheets();
}

...但为此,我需要确保从数据库中填充TimeEntries列表(EF Core不支持延迟加载)。我不想在每个请求中 .Include(x =&gt; y)所有内容,因为有时我只需要没有时间条目的员工姓名,这会影响应用程序的性能。

有人能指出我如何正确地构建这个方向吗?

修改 一种可能性(来自第一个答案的评论)将是:

{{1}}

2 个答案:

答案 0 :(得分:3)

不要尝试解决聚合的查询问题。您的聚合意味着处理命令并保护不变量。它们围绕一组数据形成一致性边界。

Employee对象是否负责保护员工时间表的完整性?如果它没有,那么这些数据不属于Employee类。

对于CRUD模型,延迟加载可能没什么用,但在我们设计聚合时通常被认为是反模式,因为它们应该尽可能小而且有凝聚力。

您是否根据时间表中的计算结果做出业务决策?是否有任何不变量可以保护?如果决定是否陈旧的时间表数据,这是否重要?如果这些问题的答案是,那么您的计算实际上只不过是一个查询。

在服务对象中放置查询很好。这些服务对象甚至可以存在于域模型之外(例如,在应用层中),但是没有严格的规则可以遵循。此外,您可以选择加载一些聚合以访问处理计算所需的数据,但直接进入数据库通常更好。这样可以更好地分离读数和读数。写道(CQRS)。

答案 1 :(得分:0)

如果我正确理解了您的问题,您可以使用技巧将服务注入您的实体,以帮助其完成工作,例如:

public class Employee()
{
    public object GetTimeSheets(ICalculatorHelper helper)
    {
    }
}

然后在您拥有员工的服务中,您将在构造函数中获取它并传递给员工类进行计算。该服务可以是Facade,例如获取所有数据并执行初始化或您真正需要的任何内容。

对于TimeEntries,你可以使用这样的函数来获取它们:

private GetTimeEntries(ICalculationHelper helper)
{
   if (_entries == null)
   {
       _entries = helper.GetTimeEntries();
   }
   return _entries;
}

当然,如果这种模式适合你,那当然取决于你的缓存策略等等。

就我个人而言,我发现很容易使用贫血类,并且在服务中有很多业务逻辑。我把一些放在物体中,例如从FirstName和LastName计算FullName。通常不涉及其他服务的东西。虽然这是一个偏好的问题。