“业务逻辑应该在服务中而不在模型中”的准确性如何?


396

情况

今天晚上早些时候,我回答了关于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
    }
}

我收到的评论:

业务逻辑实际上应该在服务中。不在模型中。

互联网怎么说?

因此,这使我开始寻找,因为我从未真正(有意识地)使用过服务层。我开始阅读“服务层”模式和“工作单位”模式,但到目前为止,我还不能说我确信必须使用服务层。

例如,Martin Fowler的这篇关于贫血域模型的反模式的文章

在领域空间中,有许多对象以名词命名,这些对象与真实领域模型所具有的丰富关系和结构相关联。当您观察行为时就会发现问题所在,并且您意识到这些对象几乎没有任何行为,这使它们仅比一堆吸气剂和塞脂剂小得多。确实,这些模型经常带有设计规则,这些规则说您不要在域对象中放置任何域逻辑。而是有一组服务对象捕获了所有域逻辑。这些服务位于域模型之上,并使用域模型存储数据。

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

在我看来,这恰好是这种情况:我提倡通过在类内部引入可做到这一点的方法来操纵对象的数据。但是我意识到这应该是给定的任何一种方式,并且可能与如何调用这些方法(使用存储库)有更多关系。

我还觉得在那篇文章中(见下文),服务层比实际的工作量大的层更被视为将工作委托给基础模型的立面

应用程序层[他的服务层名称]:定义软件应该执行的工作,并指导可表达的领域对象解决问题。该层负责的任务对业务有意义或与其他系统的应用程序层交互所必需。该层保持薄。它不包含业务规则或知识,仅协调任务并将工作委托给下一层的域对象协作。它没有反映业务情况的状态,但是可以具有反映用户或程序的任务进度的状态。

在这里加强:

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

在这里

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

这与其他谈论服务层的资源形成了鲜明的对比:

服务层应包含具有方法的类,这些方法是具有属于同一事务的动作的工作单元。

或我已经链接的问题的第二个答案

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

“解”?

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

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

结论

总的来说,这里的变化不大:控制器中的代码已移至服务层(这是一件好事,因此这种方法有一个好处)。但是,这似乎与我的原始答案没有任何关系。

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

  • 这是一种简单地从控制器中提取逻辑并将其放入服务中的方法吗?

  • 是否应该在控制器和域之间形成合同?

  • 域和服务层之间应该有一层吗?

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

业务逻辑实际上应该在服务中。不在模型中。

  • 这个对吗?

    • 如何将业务逻辑引入服务而不是模型中?

6
我将服务层视为将事务脚本不可避免的部分置于聚合根上的位置。如果我的服务层变得太复杂,以至于向我表明我朝着Anemic模型的方向发展,而我的域模型则需要关注和审查。我还尝试将逻辑放在SL中,其本质不会重复。
Pavel Voronin

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

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

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

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

Answers:


367

为了定义什么是服务的职责是,你首先需要定义什么服务是。

服务不是规范或通用软件术语。实际上,Service类名的后缀很像受害的Manager:它几乎不告诉您对象的实际作用

实际上,服务应该做的是高度特定于体系结构的:

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

  2. 在RPC样式的分布式体系结构(SOAP,UDDI,BPEL等)中,服务是物理端点的逻辑版本。它本质上是维护者希望作为公共API提供的一组操作。各种最佳实践指南解释说,服务操作实际上应该是业务级别的操作,而不是CRUD,我倾向于同意。

    但是,由于通过实际的远程服务路由所有内容会严重影响性能,因此通常最好不要让这些服务自己实际实现业务逻辑。相反,他们应该包装一组“内部”业务对象。一项服务可能涉及一个或多个业务对象。

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

    有些人错误地认为采用100线控制器方法并将其全部推入服务可以使体系结构更好。确实没有;它所做的就是添加另一个可能是不必要的间接层。实际上,控制器仍在进行工作,只是通过一个名称不正确的“ helper”对象进行的。我强烈推荐Jimmy Bogard的Wicked Domain Models演示,作为一个清晰的示例,说明如何将贫血的领域模型转变为有用的模型。它涉及仔细检查您要公开的模型以及在业务环境中实际上有效的操作。

    例如,如果您的数据库包含“订单”,并且您有“总金额”列,则可能不应该允许您的应用程序将该字段实际更改为任意值,因为(a)它是历史记录,(b)应该是由什么决定顺序,以及可能还有一些其他时间敏感的数据/规则。创建服务来管理订单不一定能解决此问题,因为用户代码仍然可以获取实际的Order对象并更改其金额。相反,订单本身应负责确保只能以安全且一致的方式对其进行更改。

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

    DDD服务的一个值得注意的方面是允许DDD服务使用事务脚本。在大型应用程序上工作时,您最终很可能会遇到实例,在该实例中,用T-SQL或PL / SQL过程完成某些工作比对域模型进行麻烦要容易得多。可以,它属于服务。

    这与服务的分层体系结构定义完全不同。服务层封装域对象;DDD服务封装了域对象中存在的任何内容,并且没有意义。

  5. 在面向服务的体系结构中,服务被认为是业务能力的技术授权。这意味着它是业务数据的某些子集的唯一所有者,并且不允许其他任何人触摸该数据-甚至不只是读取它。

    根据需要,服务实际上是SOA中的端到端主张。意思是,服务不是整个堆栈中的特定组件,您的整个应用程序(或您的整个业务)是一组并排运行的服务,除了在消息传递和UI层之外没有任何交叉。每个服务都有自己的数据,自己的业务规则和自己的UI。它们不需要相互协调,因为它们应该与业务保持一致-并且像业务本身一样,每个服务都有其自己的职责集,并且或多或少地独立于其他服务而运作。

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

    由于SOA通常热衷于消息传递,因此您以前可能打包在服务中的操作通常封装在处理程序中,但是多样性是不同的。每个处理程序处理一种消息类型,一种操作。这是对“ 单一责任原则”的严格解释,但是由于每个可能的操作都在其自己的类中,因此具有很大的可维护性。因此,您实际上并不需要集中的业务逻辑,因为命令代表业务操作,而不是技术操作。

最终,在您选择的任何体系结构中,都会有一些包含大多数业务逻辑的组件或层。毕竟,如果业务逻辑散布在各处,那么您就只有意粉代码。但是,是否将该组件称为服务,以及如何根据操作的数量或大小来设计服务,取决于您的体系结构目标。

没有正确或错误的答案,只有适用于您的情况的答案。


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

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

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

1
但是,如果我们使用100多个线路控制器(例如,该控制器接受消息-然后反序列化JSON对象,进行验证,应用业务规则,保存到db,返回结果对象),然后将某些逻辑移至一种称为service方法的逻辑上,是否可以帮助我们分别轻松地对每个部分进行单元测试?
artjom 2014年

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

40

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

再一次,术语“模型”是一个重载术语。也许您不是在说MVC模型,而是在数据传输对象(DTO)的意义上说模型。又称实体。这就是马丁·福勒所说的。

我的看法是,马丁·福勒(Martin Fowler)在谈论理想世界中的事物。在Hibernate和JPA(在Java领域)的真实世界中,DTO是一种超级泄漏的抽象。我很乐意将我的业务逻辑放在我的实体中。这会使事情变得更干净。问题在于这些实体可能以受管/缓存状态存在,这很难理解并且会不断阻止您的努力。总结一下我的观点:Martin Fowler建议使用正确的方法,但是ORM会阻止您这样做。

我认为鲍勃·马丁(Bob Martin)有一个更现实的建议,他在这段视频中给出了这一建议,该建议并非免费。他谈到要使您的DTO摆脱逻辑障碍。他们只是保存数据并将其传输到面向对象得多的另一层,而不直接使用DTO。这样可以避免泄漏的抽象咬你。具有DTO和DTO本身的层不是OO。但是一旦走出这一层,您就会像马丁·福勒(Martin Fowler)倡导的那样成为OO。

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

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

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


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

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

25

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

这是我们想出的:

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

我们最终得到以下结果:

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

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

说了这么多,我认为这与马丁·福勒所说的很接近

这些服务位于域模型之上,并使用域模型存储数据。

下图很好地说明了这一点:

http://martinfowler.com/eaaCatalog/serviceLayer.html

http://martinfowler.com/eaaCatalog/ServiceLayerSketch.gif


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

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

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

6
帮自己一个忙,杀死您的服务层。您的用户界面应直接使用您的域。我以前见过这个,您的域总是变成一堆贫血的dto,而不是丰富的模型。
安迪

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

9

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

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


2
您支持带有业务逻辑的服务层的立场很有意义,但这仍然存在一些问题。在我的帖子中,我引用了一些受人尊敬的消息来源,它们将服务层描述为没有任何业务逻辑的立面。这与您的答案形成直接对比,您能否澄清这一区别?
Jeroen Vannevel 2013年

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

8

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

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。然后,实体和调用逻辑都可以获取对验证服务的引用并重新使用它。

如果您这样做,但仍然想重用验证逻辑,则最终将其放入实体类的静态方法中:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            string message;
            if(!TryValidateSomeProperty(value, out message)) 
            {
                throw new ArgumentOutOfRangeException("value", message);
            }
            this.someProperty = value;
        }
    }

    public static bool TryValidateSomeProperty(int value, out string message)
    {
        if(value < 0)
        {
            message = "Some Property cannot be negative.";
            return false;
        }
        message = string.Empty;
        return true;
    }
}

这将使您的域模型不那么“贫乏”,并且使验证逻辑靠近属性,这很棒,但是我认为没有人真的喜欢静态方法。


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

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

6

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

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

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

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


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

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

4

您将如何在服务层中实现业务逻辑?当您从用户处付款时,您就在创建付款,而不仅仅是从属性中扣除值。

您的付款方式需要创建付款记录,增加该用户的债务,并将所有这些保留在您的存储库中。使用服务方法执行此操作非常简单,您还可以将整个操作包装在事务中。在聚合域模型中执行相同操作会带来更多问题。


2

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

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

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

这里不再讨论更长的问题,但要点是:核心的面向对象编程说,相关对象之外的任何人都不能更改该对象内的属性值,甚至“请参阅”对象内属性的值。通过将数据设为只读可以缓解这种情况。您仍然会遇到一些问题,例如当很多人以只读方式使用数据时,您必须更改数据的类型。所有的消费者都有可能不得不改变以适应这种情况。这就是为什么当您使任何人和所有人都使用API​​时,建议您不要具有公共甚至受保护的属性/数据;这就是最终发明OOP的原因。

我认为,除了已标记为已接受的答案之外,这里的大多数答案都使问题变得模糊。标有“接受”的一项是好的,但我仍然觉得有必要回答并同意,总的来说,第4条是可行的方法。

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


1

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

您为服务层引用的没有任何业务逻辑的源引入了另一层,即业务层。在许多情况下,服务层和业务层被压缩为一个。这实际上取决于您要如何设计系统。您可以分三层完成工作,并继续进行装饰并添加噪音。

理想情况下,您可以做的是模型服务,该服务包含用于在域模型上工作以保持状态的业务逻辑。您应该尝试使服务尽可能地分离。


0

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

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


0

服务层的概念可以从DDD的角度来看。Aaronaught在回答中提到了这一点,我只是在阐述一下。

常见的方法是有一个特定于客户端类型的控制器。说,它可能是网络浏览器,可能是其他应用程序,可能是功能测试。请求和响应格式可能会有所不同。因此,我将应用程序服务用作利用六边形体系结构的工具。我在这里注入特定于具体请求的基础结构类。例如,这就是我的控制器为Web浏览器请求提供服务的样子:

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

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

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

如果我正在编写功能测试,则我想使用假的付款客户端,并且可能不需要html响应。所以我的控制器看起来像这样:

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

        $responseData =
            new PurchaseOrderApplicationService(
                new FakePayPalClient(),
                new OrderRepository(),
                $data
            )
        ;

        return new JsonData($responseData);
    }
}

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

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

这是一种简单地从控制器中提取逻辑并将其放入服务中的方法吗?

没有。

是否应该在控制器和域之间形成合同?

好吧,可以这样称呼。

域和服务层之间应该有一层吗?

不。


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


-2

作为记录。

建议零售价:

  1. 模型=数据,在此进行设置和获取。
  2. 逻辑/服务=做出决定。
  3. 存储库/ 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)我们不应该害怕离开最终开发者来编写几个,而不是将其封装在一个函数中。

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

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


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

3
什么样的句子是"We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function.“?我只能引用刘易斯·布莱克“如果不是我的那匹马,那一年我就不会在大学里度过。”
玛拉基书
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.