“业务逻辑层”适合MVC应用程序在哪里?
首先,在任何人尖叫之前,我很难用一个简单的标题来概括它。 另一个标题可能是“域模型和MVC模型有什么区别?” 或“什么是模型?”
从概念上讲,我理解一个模型是视图和控制器使用的数据。 除此之外,对于模型的构成似乎有很多不同的观点。 什么是领域模型,与应用程序模型,视图模型,服务模型等等。
例如,在最近一个关于存储库模式的问题中,我被告知空白,存储库是模型的一部分。 但是,我已经读过其他观点,认为模型应该从持久模型和业务逻辑层分离。 毕竟,不是Repository模式应该将具体的持久化方法与模型分离吗? 其他人说,域模型和MVC模型是有区别的。
我们举一个简单的例子。 包含在MVC默认项目中的AccountController。 我已经阅读了几个意见,包括帐户代码是糟糕的devise,违反SRP等..如果要为MVC应用程序devise一个“适当的”成员模型,那会是什么?
你将如何分离模型中的ASP.NET服务(会员提供商,angular色提供商等)? 或者你会呢?
我看到它的方式,模型应该是“纯粹的”,也许与validation逻辑..但应该是与业务规则(validation除外)分开。 例如,假设您有一个业务规则,即在创build新帐户时必须通过电子邮件发送某人。 在我看来,这并不属于模型。 那它属于哪里?
有人在乎这个问题吗?
我做到这一点的方式 – 而不是说是对还是错,是有我的观点,然后是适用于我的观点的模型。 这个模型只与我的观点相关 – 包括数据注解和validation规则。 控制器只包含build立模型的逻辑。 我有一个服务层,其中包含所有的业务逻辑。 我的控制器调用我的服务层。 除此之外是我的存储库层。
我的域对象分开安置(实际上在他们自己的项目中)。 他们有自己的数据注释和validation规则。 我的存储库在将它们保存到数据库之前validation我的域中的对象。 因为我的域中的每个对象都从内置了validation的基类inheritance,所以我的存储库是通用的,并validation了所有内容(并要求它从基类inheritance)。
你可能会认为有两套模型是代码的重复,并且在一定程度上。 但是,有一些完全合理的情况,域对象不适用于视图。
例如在使用信用卡时 – 在处理付款时我必须要求一个cvv,但是我不能存储cvv(这样做可以罚款50,000美元)。 但是,我也希望你能够编辑你的信用卡 – 更改地址,姓名或到期date。 但编辑它时,你不会给我编号或简历,我当然也不会在页面上以纯文本的forms显示你的信用卡号码。 我的域名具有保存新信用卡所需的这些值,因为您将它们交给我,但是我的编辑模式甚至不包括卡号或cvv。
这么多层的另一个好处是,如果架构正确,您可以使用结构图或其他IoC容器,并交换部分,而不会对应用程序造成不利影响。
在我看来,控制器代码只应该是针对视图的代码。 显示这个,隐藏,等等。服务层应该为你的应用程序的业务逻辑。 我喜欢把所有的东西放在一个地方,所以很容易改变或调整一个商业规则。 存储库层应该是相对愚蠢的 – 没有业务逻辑,只能查询你的数据并返回你的域对象。 通过将视图模型从领域模型中分离出来,您在自定义validation规则方面拥有更多的灵活性。 这也意味着您不必将隐藏字段中的每个数据转储到您的视图中,并在客户端和服务器之间来回推送(或在后端重新构build它)。 然后,您的视图模型将只保存与视图相关的信息 – 并且可以自定义为视图逻辑或计数或枚举的布尔值,以便视图本身不会被复杂的逻辑语句混淆
<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && Model.SomeObject.SomeInt == 3 && ...) { %>
尽pipe一切似乎都是分散的,而且是分层次的,但它有一个被这样构build的目的。 这是完美的吗? 不是真的。 但是我更喜欢过去从控制器调用存储库的devise,并将控制器,存储库和模型中的业务逻辑混合在一起。
我经常想知道MVC元素究竟是如何适应传统的Web应用程序结构的,这些Web应用程序结构中有视图(页面),控制器,服务和数据对象(模型)。 正如你所说,有很多版本的。
我相信这个混淆是因为上面提到的被广泛接受的架构,它使用了“贫血域模型”(所谓的)反模式。 我不会详细讨论贫血症数据模型的“反模式”(你可以看看我在这里解释的东西(基于Java的,但是与任何语言相关的))。 但总之,这意味着我们的模型只包含数据,而业务逻辑则放在服务/pipe理者中。
但是,让我们假设我们有域驱动的架构 ,我们的域对象是他们所期望的方式 – 既有状态逻辑又有业务逻辑。 而在这个领域驱动的angular度来看,
- 该视图是UI
- 控制器收集UI的input,调用模型上的方法,并将响应发送回UI
- 这个模型就是我们的业务组件 – 持有这些数据,还有业务逻辑。
我想这个答案是你的主要问题。 当我们添加更多的图层时,情况会变得复杂,比如库层。 通常build议应该由放置在模型中的业务逻辑调用(因此每个域对象都有一个对存储库的引用)。 在我链接的这篇文章中,我认为这不是最好的做法。 事实上,拥有一个服务层并不是一件坏事。 顺便说一下,领域驱动devise不排除服务层,但它应该是“瘦”,只有协调域对象(所以没有业务逻辑)。
对于被广泛采用(无论好坏)的贫血数据模型范例,该模型既是服务层又是数据对象。
我的想法是,
模型 –
不应该包含业务逻辑,它应该是可插入的(WCF类似的情况)。 它被用来绑定到视图,所以它应该有属性。
商业逻辑 –
它应该放在“域服务层”,它是完全分离的层。 此外,在这里将添加一个“应用程序服务”层。
应用程序服务与域服务层交谈以应用业务逻辑,最后返回模型。
所以,Controller会要求Application Service for Model,stream程就会像这样,
Controller->Application Services(using domain services)->Model
MVC模式和Asp.net框架没有区分模型应该是什么。
MS自己的例子包括模型中的持久化类。 你关于成员在模型中的问题。 这取决于。 你的模型中的类是由什么东西所拥有的? 有谁login和显示哪些数据之间有联系? 有没有过滤可编辑的权限系统的数据部分? 是谁最后更新或编辑了你的域的对象部分,因为在别人需要看到它或后端支持?
电子邮件的例子也取决于。 您是否熟悉域事件或特别事件? 你有一个单独的服务来发送电子邮件? 发送电子邮件的行为是否是您的域的一部分,还是您系统范围之外的应用程序级别的问题? 用户界面是否需要知道电子邮件是否成功发送? 发送失败的电子邮件需要重试吗? 发送的电子邮件内容是否需要存储以获得支持或客户服务要求?
这些types的问题过于宽泛和主观,但我正在回答,所以你和投票给你的每个人都可以理解这一点。
您的需求/时间表/资源都stream失到您系统的架构中。 即使是收入模式也能起作用。 你也必须考虑你正在拍摄的模式。 DDD与持久化模型应用程序有很大不同,它们之间的所有关系对于某些应用程序也是有效的。 你正在拍摄testing应用程序? 所有这一切都有效果。