情况

今天傍晚,我回答了有关StackOverflow的问题。

问题:


编辑现有对象应该在存储库层还是在服务中进行?

例如,如果我有一个欠债的用户。我想改变他的债务。我应该在UserRepository中还是在服务中(例如BuyingService)通过获取对象,对其进行编辑并保存来实现?


我的回答:


您应该负责将一个对象更改为同一对象的责任,并使用存储库来检索该对象。


示例情况:

 class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}
 


A我收到的评论:


业务逻辑应该真正存在于服务中。不在模型中。


互联网怎么说?

所以,这让我开始搜索,因为我从未真正(有意识地)使用过服务层。
我开始阅读Service Layer模式和Unit of Work模式,但到目前为止我还不能说我确信必须使用Service Layer。

例如,马丁·福勒(Martin Fowler)在“贫血领域模型”的反模式上的文章:真正的领域模型具有丰富的关系和结构。当您查看行为时就会发现问题所在,并且您意识到这些对象几乎没有任何行为,这使它们仅比一堆吸气剂和吸气剂小得多。实际上,这些模型经常带有设计规则,这些规则说您不要在域对象中放置任何域逻辑。而是有一组服务对象捕获了所有域逻辑。这些服务基于域模型,并使用域模型存储数据。

(...)域对象中应该包含的逻辑是域逻辑-验证,计算,业务规则-随便您如何称呼它。



此处进行了增强:


服务接口。服务公开了将所有入站消息发送到的服务接口。您可以将服务接口看作是外观,向潜在的使用者公开应用程序中实现的业务逻辑(通常是业务层中的逻辑)。


这里: >

服务层应该没有任何应用程序或业务逻辑,并且应该主要集中在一些问题上。它应该包装业务层调用,以客户端可以理解的通用语言翻译域,并处理服务器与请求客户端之间的通信媒体。


有关服务层的资源:


服务层应由带有方法的类组成,这些方法是具有属于同一事务的动作的工作单元。 >或者是我已经链接的问题的第二个答案:


有时,您的应用程序将需要一些业务逻辑。另外,您可能希望验证输入,以确保没有任何有害或不良要求。此逻辑属于您的服务层。


“解决方案”?

按照此答案中的准则,我提出了以下使用Service的方法层:

 class UserController : Controller {
    private UserService _userService;

    public UserController(UserService userService){
        _userService = userService;
    } 

    public ActionResult MakeHimPay(string username, int amount) {
        _userService.MakeHimPay(username, amount);
        return RedirectToAction("ShowUserOverview");
    }

    public ActionResult ShowUserOverview() {
        return View();
    }
}

class UserService {
    private IUserRepository _userRepository;

    public UserService(IUserRepository userRepository) {
        _userRepository = userRepository;
    }

    public void MakeHimPay(username, amount) {
        _userRepository.GetUserByName(username).makePayment(amount);
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}

class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}
 


结论

所有在一起的东西不多更改为:控制器中的代码已移至服务层(这是一件好事,因此这种方法有好处)。但是,这似乎与我的原始答案没有任何关系。

我意识到设计模式是指导原则,而不是一成不变的规则,应尽可能实施。但是我还没有找到关于服务层以及如何考虑的明确解释。


是否可以简单地从控制器中提取逻辑并将其放入服务中?
是否应该在控制器和域之间形成合同?
域与服务层之间应该有一个层吗?

最后但并非最不重要的一点:遵循原始注释


业务逻辑应该真的在服务中。不在模型中。




这是正确的吗?


我该如何在服务而非模型中引入业务逻辑?




评论

我将服务层视为将事务脚本不可避免的部分置于聚合根上的位置。如果我的服务层变得太复杂,以至于向我表明我朝着Anemic模型的方向前进,那么我的域模型就需要引起注意和审查。我还尝试将逻辑放在SL中,该逻辑不会因其性质而重复。

我认为服务是模型层的一部分。我认为这是错误的吗?

我的经验法则是:不要依赖您不需要的东西;否则,我可能会(通常受到负面影响)受到我不需要的部分的更改的影响。结果,我最终得到了许多明确定义的角色。只要所有客户端都使用它,我的数据对象就会包含行为。否则,我会将行为转移到实现所需角色的类中。

应用程序流控制逻辑属于控制器。数据访问逻辑属于存储库。验证逻辑属于服务层。服务层是ASP.NET MVC应用程序中的另一层,用于中介控制器和存储库层之间的通信。服务层包含业务验证逻辑。 repository.asp.net/mvc/overview/older-versions-1/models-data / ...

恕我直言,正确的OOP样式域模型应确实避免使用“业务服务”,并将业务逻辑保留在模型中。但是实践表明,使用独特命名的方法创建巨大的业务层并围绕整个方法进行事务(或工作单元)非常诱人。在一个业务服务方法中处理多个模型类比计划这些模型类之间的关系要容易得多,因为这样您将要回答一些难题:聚合根在哪里?我应该在哪里启动/提交数据库事务?等

#1 楼

为了定义服务的职责,首先需要定义服务的职责。

服务不是规范的或通用的软件术语。实际上,类名的后缀Service很像恶意的Manager:它几乎不告诉您对象实际执行的操作。高度特定于体系结构:


在传统的分层体系结构中,服务实际上是业务逻辑层的同义词。它是UI和数据之间的层。因此,所有业务规则都将纳入服务中。数据层应仅了解基本的CRUD操作,UI层应仅处理与业务对象之间的表示DTO映射。

在RPC样式的分布式体系结构(SOAP,UDDI, BPEL等),该服务是物理端点的逻辑版本。它本质上是维护者希望作为公共API提供的一组操作。各种最佳实践指南解释说,服务操作实际上应该是业务级别的操作,而不是CRUD,我倾向于表示同意。 ,通常最好不要让这些服务自己实际实现业务逻辑。相反,他们应该包装一组“内部”业务对象。一项服务可能涉及一个或多个业务对象。


在MVP / MVC / MVVM / MV *体系结构中,根本不存在服务。或者,如果这样做,则该术语用于指代可以注入到控制器或视图模型中的任何通用对象。业务逻辑在您的模型中。如果要创建“服务对象”来编排复杂的操作,则可以将其视为实现细节。遗憾的是,很多人都这样实现MVC,但由于模型本身不执行任何操作,而只是UI的一堆属性,因此它被视为反模式(贫血域模型)。

有些人们错误地认为采用100线控制器方法并将其全部推入服务可以以某种方式改善体系结构。确实没有;它所做的只是添加另一个可能是不必要的间接层。实际上,控制器仍在进行工作,只是通过一个名称不正确的“ helper”对象进行的。我强烈推荐Jimmy Bogard的Wicked Domain Models演示,作为一个清晰的示例,说明如何将贫血的领域模型转变为有用的模型。它涉及仔细检查要公开的模型以及在业务上下文中实际上有效的操作。可能实际上不应该允许将该字段更改为任意值,因为(a)它的历史记录,(b)应该由顺序以及其他一些对时间敏感的数据/规则来确定。创建服务来管理订单并不一定能解决此问题,因为用户代码仍然可以获取实际的Order对象并更改其金额。相反,订单本身应负责确保只能以安全且一致的方式对其进行更改。


在DDD中,服务专门用于当您执行的操作不正确地属于任何聚合根的情况。您在这里必须小心,因为经常需要服务可能意味着您没有使用正确的根目录。但是,假设您这样做了,则可以使用服务来协调多个根之间的操作,或者有时用于处理根本不涉及域模型的问题(例如,将信息写入BI / OLAP数据库)。 >
DDD服务的一个值得注意的方面是允许DDD服务使用事务脚本。在大型应用程序上工作时,您最终很可能会遇到实例,在该实例中,用T-SQL或PL / SQL过程完成某件事要比对域模型大惊小怪。没关系,它属于服务。

这与服务的分层体系结构定义完全不同。服务层封装域对象; DDD服务封装了域对象中不存在的所有内容,并且没有任何意义。业务能力。这意味着它是业务数据的某些子集的专有所有者,并且不允许其他任何人触摸该数据-甚至不能只读取它。结束SOA中的命题。意思是,服务不是整个堆栈中的特定组件,您的整个应用程序(或您的整个业务)是一组并排运行的服务,除了在消息传递和UI层之外没有任何交叉。每个服务都有自己的数据,自己的业务规则和自己的UI。它们不需要相互协调,因为它们应该与业务保持一致-就像业务本身一样,每个服务都有其自己的职责集,并且或多或少地独立于其他服务而运作。

因此,根据SOA定义,服务中包含任何地方的每个业务逻辑,但是整个系统也包含在其中。 SOA中的服务可以具有组件,也可以具有端点,但是将任何代码段称为服务都是相当危险的,因为它与原始“ S”的含义相冲突。

SOA通常非常热衷于消息传递,您以前可能打包在服务中的操作通常封装在处理程序中,但是多样性不同。每个处理程序处理一种消息类型,一种操作。这是对“单一责任原则”的严格解释,但由于每个可能的操作都在其自己的类中,因此具有很大的可维护性。因此,您实际上并不需要集中的业务逻辑,因为命令代表的是业务操作,而不是技术操作。具有大多数业务逻辑。毕竟,如果业务逻辑散布在各处,那么您就只有意粉代码。但是,是否将该组件称为服务,以及如何根据操作的数量或规模来设计服务,取决于您的体系结构目标。

没有正确或错误的答案,只有适用的方法根据您的情况。

评论


感谢您的详尽回答,您已经澄清了我所能想到的一切。虽然其他答案也都可以提供出色的质量,但我相信这个答案是最重要的,因此我会接受这个答案。我将在此处添加其他答案:精美的质量和信息,但可悲的是,我只能给您点赞。

– Jeroen Vannevel
13年11月14日在4:19

我不同意这样的事实,在传统的分层体系结构中,服务是业务逻辑层的同义词。

– CodeART
13年11月14日在8:46

@CodeART:它是三层体系结构。我已经看到了4层体系结构,其中在表示层和业务层之间有一个“应用程序层”,有时也称为“服务”层,但是老实说,我见过的唯一成功实现的地方是庞大来自SAP或Oracle的可无限配置的“为您量身定制的全业务”产品,我认为这里真的不值得一提。如果您愿意,我可以澄清一下。

– Aaronaught
13年11月14日在12:36



@Aaronaught我想澄清一件事,如果我们有通过ORM映射到db的域对象,并且其中没有业务逻辑是贫血的领域模型还是没有?

– artjom
2014年1月7日在8:06

@Aaronaught,你怎么变得如此聪明?说真的最佳做法书?

–最大
17年12月24日在12:18

#2 楼

至于你的头衔,我认为这个问题没有道理。 MVC模型由数据和业务逻辑组成。说逻辑应该在服务中而不是模型中,就像在说:“乘客应该坐在座位上,而不是坐在汽车上”。

然后,术语“模型”是一个重载术语。也许您不是在说MVC模型,而是在数据传输对象(DTO)的意义上说模型。又称实体。这就是马丁·福勒(Martin Fowler)所谈论的。在Hibernate和JPA(在Java领域)的真实世界中,DTO是一种超级泄漏的抽象。我很乐意将我的业务逻辑放在我的实体中。这会让事情变得更干净。问题在于这些实体可能以受管/缓存状态存在,这很难理解并且会不断阻止您的努力。总结一下我的观点:Martin Fowler建议使用正确的方法,但是ORM会阻止您这样做。他谈到要使您的DTO摆脱逻辑障碍。他们只是保存数据并将其传输到面向对象得多的另一层,而不直接使用DTO。这避免了泄漏的抽象咬你。具有DTO和DTO本身的层不是OO。但是一旦走出这一层,您就可以像Martin Fowler所提倡的那样成为OO。

这种分离的好处是它抽象了持久层。您可以从JPA切换到JDBC(反之亦然),而不必更改任何业务逻辑。它仅取决于DTO,而不在乎这些DTO的填充方式。

要稍微改变主题,您需要考虑以下事实:SQL数据库不是面向对象的。但是ORM通常每个表都有一个实体-这是一个对象。因此,从一开始,您就已经输掉了一场战斗。以我的经验,您永远无法以面向对象的方式以所需的确切方式表示实体。

至于“服务”,鲍勃·马丁将反对开设名为FooBarService的类。那不是面向对象的。服务做什么?与FooBars相关的任何东西。也可以将其标记为FooBarUtils。我认为他会提倡服务层(更好的名称是业务逻辑层),但是该层中的每个类都有一个有意义的名称。

评论


同意您对ORM的观点;它们散布了一个谎言,即您将实体直接与它们映射到db,而实际上一个实体可能存储在多个表中。

–安迪
2014年11月20日在1:29

@Daniel Kaplan您知道鲍勃·马丁视频的更新链接是什么吗?

–布莱恩·莫雷蒂(Brian Morearty)
18年1月18日在22:29

#3 楼

我现在正在进行未开发的项目,就在昨天,我们几乎没有做出任何建筑决策。有趣的是,我不得不重新阅读“企业应用程序体系结构模式”的几章。

这是我们想到的:查询和更新数据库。该层通过可注射的存储库暴露。
域层。这就是业务逻辑所在。该层使用可注入存储库,并负责大多数业务逻辑。这是我们将进行全面测试的应用程序的核心。
服务层。该层与域层对话,并服务于客户端请求。在我们的例子中,服务层非常简单-将请求中继到域层,处理安全性和其他一些交叉问题。这与MVC应用程序中的控制器没什么不同-控制器又小又简单。
客户端层。通过SOAP与服务层对话。

我们得出以下结果:

客户端->服务->域->数据

我们可以用合理的工作量替换客户端,服务或数据层。如果您的域逻辑存在于服务中,并且您已决定要替换甚至删除服务层,则必须将所有业务逻辑移到其他位置。这样的要求很少见,但是可能会发生。服务位于域模型的顶部,并使用domain
模型存储数据。


下面的图很好地说明了这一点: /martinfowler.com/eaaCatalog/serviceLayer.html





评论


您昨天才决定使用SOAP吗?这是一个要求还是您只是没有更好的主意?

– JensG
13年11月14日,0:15



REST不会满足我们的要求。 SOAP或REST,这与答案没有任何区别。据我了解,服务是通向领域逻辑的门户。

– CodeART
13年11月14日在8:17

完全同意网关。 SOAP是(标准化的)膨胀软件,所以我不得不问。是的,对问题/答案也没有影响。

– JensG
13年11月14日17:44



帮自己一个忙,杀死您的服务层。您的用户界面应直接使用您的域。我之前已经看过这一点,并且您的域总是变成一堆贫乏的dto,而不是丰富的模型。

–安迪
2014年11月20日,下午1:32

这些幻灯片涵盖了有关服务层的解释,并且上面的图形非常简洁:slideshare.net/ShwetaGhate2/…

– Marc Juchli
2015年1月9日,12:10

#4 楼

这是真正取决于用例的事情之一。服务层的总体目的是将业务逻辑整合在一起。这意味着几个控制器可以调用相同的UserService.MakeHimPay(),而无需真正关心付款的方式。服务中发生的事情可能就像修改对象属性一样简单,或者可能正在做与其他服务打交道的复杂逻辑(即,调用第三方服务,调用验证逻辑,甚至只是将某些内容保存到数据库中)。 )

这并不意味着您必须从域对象中剥离所有逻辑。有时,让域对象上的方法自己进行一些计算就更有意义了。在最后一个示例中,服务是存储库/域对象上的冗余层。它提供了一个很好的缓冲来应对需求更改,但这实际上不是必需的。如果您认为需要服务,请尝试使用简单的“在对象Y上修改属性X”逻辑而不是域对象。域类的逻辑倾向于落入“从字段计算此值”,而不是通过getter / setter公开所有字段。

评论


您支持带有业务逻辑的服务层的立场很有意义,但这仍然存在一些问题。在我的帖子中,我引用了一些值得尊敬的消息来源,它们将服务层描述为没有任何业务逻辑的立面。这与您的答案形成鲜明对比,您是否可以澄清这种差异?

– Jeroen Vannevel
13年11月11日在5:18

我发现它确实取决于业务逻辑的类型和其他因素,例如所使用的语言。某些业务逻辑不适用于域对象。一个示例是在将结果从数据库中拉回之后对结果进行过滤/排序。它是业务逻辑,但对域对象没有意义。我发现服务最适合用于简单逻辑或转换结果,而域上的逻辑在处理保存数据或从对象计算数据时最有用。

–火神传说
13年11月12日在4:06

#5 楼

说明程序员为何不将域逻辑放入域对象的最简单方法是,他们通常会遇到“我将验证逻辑放在哪里?”的情况。以这个域对象为例:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            if(value < 0) throw new ArgumentOutOfRangeException("value");
            this.someProperty = value;
        }
    }
}


所以设置器中有一些基本的验证逻辑(不能为负)。问题是您不能真正重用此逻辑。在某个屏幕,ViewModel或Controller确实需要对域对象进行更改之前,需要进行验证,因为它需要在用户单击“保存”按钮之前或单击时通知用户无法执行此操作,以及为什么。调用setter时测试异常是一个丑陋的技巧,因为您确实应该在开始事务之前就已经完成了所有验证。这就是人们将验证逻辑提供某种服务的原因,例如如MyEntityValidator。然后,实体和调用逻辑都可以获取对验证服务的引用并重新使用它。

如果您不这样做,但仍想重新使用验证逻辑,则结束将其放到实体类的静态方法中:很棒,但是我认为没有人真的喜欢静态方法。

评论


那么您认为验证的最佳解决方案是什么?

– Flashrunner
2016年9月5日在16:26

@Flashrunner-我的验证逻辑明确地在业务逻辑层中,但是在某些情况下,在实体层和数据库层中也是重复的。业务层通过通知用户等来很好地处理它,但是其他层只是抛出错误/异常,并防止程序员(我自己)犯破坏数据的错误。

–斯科特·惠特洛克
16年5月5日在16:41

@ScottWhitlock您认为将验证逻辑重复或对您的案例重复三倍是个好主意吗?

–法比安·皮克内(Fabian Picone)
4月28日上午10:14

@FabianPicone-具有实体模型并不排除某些进程直接访问数据库,因此我想对数据库施加约束。但是,允许某人创建一个实体并将其提交到违反该约束的数据库是无效的,并且会生成神秘的错误消息,因此我在实体模型中复制了该约束,并为实体模型用户(程序员)抛出了异常。那位其他程序员不能用用户的母语显示我的异常消息,或者不希望Web服务调用的开销查看我的逻辑是否引发异常。

–斯科特·惠特洛克
4月28日18:54

#6 楼

我认为,如果您阅读Martin Fowler的Anemic Domain Model文章,答案是明确的。

从领域模型中删除作为领域的业务逻辑本质上是在打破面向对象的设计。

让我们回顾一下最基本的面向对象概念:对象封装数据和操作。例如,关闭帐户是帐户对象应自行执行的操作。因此,让服务层执行该操作不是面向对象的解决方案。它是程序性的,也是马丁·福勒(Martin Fowler)在谈论贫血领域模型时所指的内容。

如果您有一个服务层关闭帐户,而不是关闭帐户对象,您没有真实的帐户对象。您的帐户“对象”仅仅是一个数据结构。正如马丁·福勒(Martin Fowler)所建议的那样,最终得到的是一堆带有吸气剂和吸气剂的袋子。

评论


编辑。我实际上发现这是一个很有帮助的解释,并且认为它不值得一票。

– BadHorsie
16年6月22日在14:59

富裕模式在很大程度上存在可忽略的缺点。这就是开发人员将任何与模型略相关的东西引入其中的原因。开/关状态是帐户的属性吗?那老板呢?还有银行?帐户应该引用它们吗?我会通过与银行交谈或直接通过帐户来关闭帐户吗?对于贫乏的模型,这些连接不是模型的固有部分,而是在其他类(称为服务或管理器)中使用这些模型时创建的。

–Hubert Grzeskowiak
19年5月23日在8:33

#7 楼

您将如何在服务层中实现业务逻辑?当您向用户付款时,您不仅要从财产中扣除价值,还需要创建付款。所有这些都在您的存储库中。使用服务方法执行此操作非常简单,您还可以将整个操作包装在事务中。在聚合域模型中执行相同操作会遇到更多问题。

评论


他没有创建付款记录。这就是为什么在他的案例中,这种业务逻辑应该存在于实体中。但是,如果他必须创建付款记录-没错,付款业务逻辑将存在于其他地方。

–anton1980
2月12日下午3:48

#8 楼

tl; dr版本:
我的经验和观点认为,任何具有业务逻辑的对象都应该是域模型的一部分。数据模型可能应该没有任何逻辑。服务可能应该将两者结合在一起,并处理跨领域的问题(数据库,日志记录等)。但是,公认的答案是最实用的答案。

较长的版本(已被其他人暗示)是对“模型”一词的模棱两可。帖子就像在相同的那样在数据模型和域模型之间切换,这是一个非常常见的错误。

实际上,您不应该拥有对任何域对象进行更改的服务;它实际上是一个服务。这样做的原因是,您的服务可能会对对象上的每个属性都具有某种方法,以便更改该属性的值。这是一个问题,因为这样,如果您有对象的接口(甚至没有接口),该服务将不再遵循开放式封闭原则。相反,每当您向模型中添加更多数据时(无论域还是数据),最终都必须向服务中添加更多功能。有一些解决方法,但这是我看到“企业”应用程序失败的最常见原因,尤其是当那些组织认为“企业”的意思是“为系统中的每个对象都有接口”时。您是否可以想象只为模型上的单个属性添加一个新方法到一个接口,然后添加到两个或三个不同的实现(应用程序内一个,模拟实现,调试一个内存内?)?听起来对我来说是一个可怕的主意。

这里不再讨论更长的问题,但要点是:核心的面向对象程序设计说,相关对象之外的任何人都不能更改该对象内属性的值,甚至“请参阅”对象内属性的值。可以通过将数据设置为只读来缓解这种情况。您仍然会遇到一些问题,例如当很多人以只读方式使用数据时,您必须更改数据的类型。所有的消费者都有可能不得不改变以适应这种情况。这就是为什么当您使任何人和所有人都使用API​​时,建议您不要具有公共甚至受保护的属性/数据;这是OOP最终被发明的原因。
我认为,除了那些已被普遍接受的答案之外,这里的大多数答案都笼罩着问题。标记为接受的一个是好的,但是我仍然觉得需要回复并同意,一般来说,项目符号4是可行的方法。


在DDD中,服务专门用于当您执行的操作不正确地属于任何聚合根时。您在这里必须小心,因为经常需要服务可能意味着您没有使用正确的根目录。但是假设您这样做了,则可以使用服务来协调多个根之间的操作,或者有时用于处理根本不涉及域模型的问题...


#9 楼

答案是,这取决于用例。但是在大多数通用场景中,我会坚持服务层中的业务逻辑。您提供的示例非常简单。但是,一旦您开始考虑分离的系统或服务并在其之上添加事务行为,您就真的希望它作为服务层的一部分发生。

为服务层引用的源中没有任何业务逻辑都会引入另一层,即业务层。在许多情况下,服务层和业务层被压缩为一个。这实际上取决于您要如何设计系统。您可以分三层完成工作,并不断进行装饰和添加噪音。您应该尝试使服务尽可能地分离。

#10 楼

服务层的概念可以从DDD的角度来看。 Aaronaught在回答中提到了这一点,我只是在此稍作阐述。说,它可能是一个网络浏览器,可能是其他应用程序,可能是功能测试。请求和响应格式可能会有所不同。因此,我将应用程序服务用作利用六边形体系结构的工具。我在其中注入特定于具体请求的基础结构类。例如,这就是我的控制器为Web浏览器提供服务的样子:而且可能我不需要html响应。所以我的控制器看起来像这样:

class WebBroserController
{
    public function purchaseOrder()
    {
        $data = $_POST;

        $responseData =
            new PurchaseOrderApplicationService(
                new PayPalClient(),
                new OrderRepository()
            )
        ;

        $response = new HtmlView($responseData);
    }
}


所以应用程序服务是我为运行业务逻辑而设置的环境。在这里调用模型类-与基础结构实现无关。

因此,从这个角度回答您的问题:


这是一种简单的方法


编号。


是否应该订立合同?在控制器和
域之间?


好吧,可以这样称呼。


在控制器之间应该有一层域和服务层?


否。


尽管完全拒绝使用任何类型的服务,但存在一种根本不同的方法。例如,大卫·韦斯特(David West)在他的《对象思维》一书中声称,任何对象都应该具有所有必要的资源来完成其工作。例如,这种方法导致丢弃任何ORM。

评论


这个答案很典型,解释了具有服务抽象(例如类,模块)的两个很好的理由:#1可测试的单元; #2可重用性。两者都可以更改,并且可以与实体隔离(而不接触实体)重用。

– hc_dev
4月11日10:58

#11 楼

在MVC中,模型被定义为业务逻辑。除非他不使用MVC,否则声称它应该在其他地方是不正确的。我认为服务层类似于模块系统。它使您可以将一组相关功能捆绑到一个不错的程序包中。该服务层的内部结构将具有与您的工作相同的模型。


该模型由应用程序数据,业务规则,逻辑和功能组成。
http: //en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller


#12 楼

作为记录。

SRP:


Model = Data,设置程序和获取程序在这里。
逻辑/服务=这里做决定。
存储库/ DAO =这里我们永久存储或检索信息。

在这种情况下,可以执行以下步骤:

如果债务不需要进行任何计算:

userObject.Debt = 9999;


但是,如果需要进行一些计算,则:

userObject.Debt= UserService.CalculateDebt(userObject)


,或者

UserService.UpdateDebt(userObject)


但是,如果计算是在持久层中完成的,那么这样的存储过程则

UserRepository.UpdateDebt(userObject)


在这种情况下,我想从数据库中检索用户并更新债务,我们应该分几个步骤(实际上是两个步骤)进行操作,不需要将其包装/封装到服务的函数中。

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)


如果需要存储它,那么我们可以添加第三步

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)
UserRepository.Save(userobject);


关于提出的解决方案

a)我们不应该害怕让最终开发人员编写一些而不是将其封装在af中。

b)关于界面,一些开发人员喜欢界面,它们很好,但在某些情况下根本不需要界面。

c)目标服务就是创建一个没有属性的服务,主要是因为我们可以使用Shared / Static函数。单元测试也很容易。

评论


这如何回答以下问题:“业务逻辑应该在服务中而不在模型中”的准确性如何?

– gna
2014-09-11 12:36



什么样的句子是“我们不应该害怕让最终开发人员编写一些而不是将其封装在函数中”。我只能引用刘易斯·布莱克(Lewis Black)的话:“如果不是为了我的马,那年我就不会在大学里度过”。

–玛拉基
2014年10月7日在21:42