简体   繁体   English

了解域实体中的域服务与应用程序服务与行为(方法)

[英]Understanding Domain Services vs. Application Services vs. Behavior (Methods) in Domain Entities

We've been working on developing an architecture for a layered application. 我们一直致力于为分层应用程序开发架构。 We are planning to have an ASP.NET MVC presentation layer that serves both mobile browsers and 'normal' browsers. 我们计划建立一个ASP.NET MVC表示层,同时为移动浏览器和“普通”浏览器提供服务。 That is, we need the same ASP.NET MVC presentation layer to serve up views optimized for mobile browsers if the user navigates to the site on a mobile device, while, at the same time, serve up 'regular' views that are optimized for desktop/laptop browsers if the user is using their desktop/laptop computer. 也就是说,如果用户在移动设备上导航到该站点,我们需要相同的ASP.NET MVC表示层来提供针对移动浏览器优化的视图,同时提供针对经过优化的“常规”视图桌面/笔记本电脑浏览器,如果用户使用他们的台式机/笔记本电脑 At the moment, we're not planning on supporting native mobile apps, but we may add that capability in the future. 目前,我们还没有计划支持原生移动应用,但我们可能会在未来添加该功能。 Additionally, the architecture will have layers for the domain model - domain entities, domain services, etc., application services, and a data layer using EF code-first POCOs and the repository and unit-of-work patterns – all standard domain model type of design. 此外,该体系结构将具有域模型的层 - 域实体,域服务等,应用程序服务和使用EF代码优先POCO以及存储库和工作单元模式的数据层 - 所有标准域模型类型设计。

However, I've been trying to figure out the difference between domain services, application services, and behavior (methods) in domain entities as it applies to our system that we're developing. 但是,我一直试图弄清楚域实体中的域服务,应用程序服务和行为(方法)之间的区别,因为它适用于我们正在开发的系统。 Below is a high-level use case in our system for examination. 以下是我们的系统中的高级用例。

We have Reports and ReportGroups. 我们有Reports和ReportGroups。 There is a many-to-many relationship between these two entities. 这两个实体之间存在多对多关系。 The DB tables are: Reports <-> Jct_Reports_ReportGroups <-> ReportGroups, with the domain entities looking something like: 数据库表是:Reports < - > Jct_Reports_ReportGroups < - > ReportGroups,域实体看起来像:

Report class :
    public string ReportName { get; set; }
    ...
    public virtual ICollection<ReportGroup> ReportGroups { get; set; }

ReportGroup class :
    public string GroupName { get; set; }
    ...
    public virtual ICollection<Report> Reports { get; set; }

Of course, a user could create a report (without creating a report group), and they could create a report group that contains one or many reports. 当然,用户可以创建报告(无需创建报告组),他们可以创建包含一个或多个报告的报告组。 Also, a user can delete a report. 此外,用户可以删除报告。 When this happens, a number of things occur. 当发生这种情况时,会发生许多事情。 First, we check to see if the report is in any report groups (a single report can be in many different report groups). 首先,我们检查报告是否在任何报告组中(单个报告可以在许多不同的报告组中)。 If the report is found in any report groups, we go through each report group and remove the report from the report group (by deleting a row out of the Jct_Reports_ReportGroups table). 如果在任何报告组中找到报告,我们将遍历每个报告组并从报告组中删除报告(通过删除Jct_Reports_ReportGroups表中的一行)。 Then, for each report group that contained the report to be deleted, we check to see if there are any other reports left in the report group. 然后,对于包含要删除的报告的每个报告组,我们会检查报告组中是否还有其他报告。 If there are no other reports in the report group, we also delete the report group (by deleting the row from the ReportGroups table). 如果报告组中没有其他报告,我们还会删除报告组(通过删除ReportGroups表中的行)。 If there are other reports left in the report group, we don't delete the report group. 如果报告组中还有其他报告,我们不会删除报告组。 If all these operations succeed, we remove the report to be deleted as selected by the user (by deleting the row in the Reports table). 如果所有这些操作都成功,我们将删除用户选择的要删除的报告(通过删除Reports表中的行)。 Finally, the user is displayed a message saying whether the report delete succeeded or not. 最后,向用户显示一条消息,指出报告删除是否成功。

I hope that's an adequate use case to help understand just a tiny bit of our overall application. 我希望这是一个足够的用例来帮助理解我们的整体应用程序中的一小部分。

I've read somewhere that domain services encapsulate business logic that doesn't naturally fit within a domain object, and are NOT typical CRUD operations, while application services are used by external consumers to talk to your system - if consumers need access to CRUD operations, they would be exposed here. 我在某处读过域服务封装了自然不适合域对象的业务逻辑,并且不是典型的CRUD操作,而外部消费者使用应用程序服务与您的系统通信 - 如果消费者需要访问CRUD操作,他们会暴露在这里。 But, I'm just not understanding what methods (business logic) would go in the POCO domain entities, what business logic would be considered domain services and contained in the business layer, and what business logic would be considered an application service contained in an application services layer. 但是,我只是不了解POCO域实体中将采用哪些方法(业务逻辑),哪些业务逻辑将被视为域服务并包含在业务层中,以及哪些业务逻辑将被视为包含在应用服务层。 So, my main question is: given the use case above, what would be the recommendation on how to break out domain services vs. application services vs. behavior (methods) in domain entities? 所以,我的主要问题是:鉴于上面的用例,关于如何在域实体中突破域服务与应用服务与行为(方法)的建议是什么?

That's quite a bit of a question. 这是一个很有问题的问题。

I prefer the following non-strict guidelines for myself: 我更喜欢以下非严格的指导方针:

Application service : Imagine we have a set of use cases which describe a user interaction with the system. 应用程序服务 :想象一下,我们有一组用例描述用户与系统的交互。 Let's say we have Login and Cash transfer cases with specific inputs and outputs; 假设我们有LoginCash transfer案例,具有特定的输入和输出; also they describe success and fail scenarios. 他们还描述了成功和失败的情景。 Having that, we in general have an application service specification: I'd expose both LoginResul Login(name, pass) and TransferResult CashTrasnfer(from, to, amount) methods, with more or less the same in's and out's and success/fail behaviour. 有了这个,我们通常都有一个应用程序服务规范:我公开了LoginResul Login(name, pass)TransferResult CashTrasnfer(from, to, amount)方法,在out和out中都有或多或少相同的成功/失败行为。

It's quite obvious that in order to realize use cases, application service calls BL (but adds security and other app-specific checks). 很明显,为了实现用例,应用程序服务调用BL(但增加了安全性和其他特定于应用程序的检查)。

Domain Service : just as you describe: 'doesn't naturally fit within a domain object'. 域服务 :正如您所描述的:'自然不适合域对象'。

Having CashTrasnfer use case, we have to: 拥有CashTrasnfer用例,我们必须:

  • load account 'from' 加载帐户'从'
  • load account 'to' 加载帐户'到'
  • check 'from' balance' //error could be returned 检查'来自'余额'//可能会返回错误
  • verify accounts access (locked, etc.)//error could be returned 验证帐户访问权限(锁定等)//可能会返回错误
  • withdraw 'amount' from 'from' 从“从”中提取“金额”
  • put the amount to 'to' 把金额加到'到'
  • (ideally, that should be a single 'business' transaction). (理想情况下,这应该是单一的“商业”交易)。

It is obvious that this function doesn't fit Account entity - so it would be nice to have a special TransferService for that. 很明显,这个函数不适合Account实体 - 所以最好有一个特殊的TransferService


Specifically about Report/ReportGroup functionality - it could be Entity scope, as there are no non-logical references (all items are reachable from report): 特别是关于Report / ReportGroup功能 - 它可以是实体范围,因为没有非逻辑引用(所有项都可以从报告中访问):

Report.Remove() {
    foreach(group in Groups) {
        group.RemoveReport(this);
    }
    repository.Remove(this);
} 
Group.RemoveReport(report) {
    reports.Remove(report);
    if(reports.Count == 0)
        repository.Remove(this);
}

or split between a report service and entities (referencing repositories could be problematic): 或者在报表服务和实体之间拆分(引用存储库可能会有问题):

Report.RemoveFromAllGroups() {
   foreach(group in Groups) {
        group.RemoveReport(this);
        if(group.IsEmpty)
            //add to collection to return
    }
}
Service.Remove(report) 
{
    var emptyGroups = report.RemoveFromAllGroups();
    reportRepo.Remove(report);
    groupRepo.Remove(emptyGroups);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM