应用程序逻辑(初始化(路由注册、IoC 配置等)、特定于 Web 的扩展、MVC 过滤器、控制器基类、视图引擎等)
X.Application.Models
查看模型类
X.Application.Services
可以通过访问存储库或域服务返回 ViewModel 的服务类,以及相反的更新方式
X.Application.Web 的其他方式
控制器、视图和静态资源
其中一些可以组合,但将它们分开可以更轻松地定位内容并确保尊重层边界。
用于显示产品购物车的典型控制器操作可能如下所示:
public virtual ActionResult ProductCart()
{
var applicationService = <obtain or create appropriate service instance>
var userID = <obtain user ID or similar from session state>
var viewModel = applicationService.GetProductCartModel( userID );
return View( "Cart", viewModel );
}
用于将产品添加到购物车的典型控制器操作可能如下所示:
public virtual ActionResult AddProductToCart( int productID )
{
var domainService = <obtain or create appropriate service instance>
var userID = <obtain user ID or similar from session state>
var response = domainService.AddProductToCart( userID, productID );
return Json( new { Success = response.Success, Message = response.Message } );
}
I typically structure my MVC solutions in a way resembling the following:
X.Core
general extension methods, logging, and other non-web infrastructure code
X.Domain
domain entities and repositories
X.Domain.Services
domain services for orchestrating complex domain operations, such as adding a product to a shopping cart
X.Application.Core
application logic (initialization (route registration, IoC configuration, etc.), web-specific extensions, MVC filters, controller base classes, view engines, etc.)
X.Application.Models
view model classes
X.Application.Services
service classes that can return ViewModels by accessing repositories or domain services, as well as the other way around for updates
X.Application.Web
controllers, views and static resources
Some of these could be combined, but having them separate makes it easier to locate stuff and to ensure your layer boundaries are respected.
A typical controller action for showing the product cart might look like this:
public virtual ActionResult ProductCart()
{
var applicationService = <obtain or create appropriate service instance>
var userID = <obtain user ID or similar from session state>
var viewModel = applicationService.GetProductCartModel( userID );
return View( "Cart", viewModel );
}
A typical controller action for adding a product to the shopping cart might thus look something like this:
public virtual ActionResult AddProductToCart( int productID )
{
var domainService = <obtain or create appropriate service instance>
var userID = <obtain user ID or similar from session state>
var response = domainService.AddProductToCart( userID, productID );
return Json( new { Success = response.Success, Message = response.Message } );
}
我的领域层(实体、业务逻辑等)没有依赖项。它没有引用我的 Web 项目或我的存储库项目。它通常被称为 POCO 或普通旧 C#/CLR 对象。
编辑:我在您的评论之一中注意到您正在使用 EF。如果不使用称为 Code First 的东西(去年 8 月检查时处于社区技术预览状态),EF 就不支持 POCO。仅供参考。
I also read Sanderson's first version of his book and it was fantastic - a very easy way to pick up and start using ASP.NET MVC. Unfortunately, you can't jump straight from the concepts in his book to writing a large maintainable application. One of the biggest hurdles is figuring out where to put your business logic and other concerns that lie between the UI and persistent storage (a concept called Separation of Concerns or SOC).
If you are interested, consider reading up on Domain Driven Design. I won't suggest that I know it perfectly, but serves as a good transition from Sanderson's sample applications into something that successfully separates UI concerns, business logic, and storage concerns.
My solution has a separate service layer. The controllers communicate with the service layer (using Dependency Injection - Ninject). The service layer has access to my domain objects / business logic and my repositories (NHibernate - also spun up with Ninject). I have very little logic in my views or controllers - the controllers serve a purpose of coordinator and I strive to keep my controller actions as thin as possible.
My domain layer (entities, business logic, etc.) has no dependencies. It does not have references to my Web project or to my Repository project. It is what is often referred to as POCO or Plain Old C#/CLR Objects.
EDIT: I noticed in one of your comments you are using EF. EF does not support POCO without using something called Code First (was in Community Technology Preview status when I checked last August). Just FYI.
The reality is that there's no silver bullet to this answer, and it really is a contextual question at heart. I like to separate things as much as possible, and business logic is, if you think about it, just another layer in an app.
I worked up a BDD-inspired decisioning engine and released it as an OSS project available via NuGet. The project is called NDecision, and you can read about it on the NDecision project home page.
NDecision makes decision-tree business logic quite simple to implement, and if you're a fan of Gherkin syntax and Fluent coding practices you'll feel right at home using it. The code snapshot below is from the project site, and demonstrates how business logic could be implemented.
This might not be a solution for you, but the idea is, if you already have your domain model in one assembly - a great idea and common practice as suggested in another answer earlier, there's no reason why you can't have another assembly with your "decision tree." NDecision was written with that in mind, to separate the logic into one independent layer.
If you want scalability, it's bad practice to use data access objects directly from the GUI. So in the example you mentioned, I think you should replace the repository that directly reads or writes to the DB by some business logic class that supports business operations.
I'm trying to learn MVC with ASP.Net and am reading Steve Sanderson's book. One thing I'm confused about is where to place the business logic?
In the model (M in MVC), or domain layer. These are a separate set of types (preferably in a separate project) that represent domain models, services and repositories.
发布评论
评论(5)
我通常以类似于以下的方式构建我的 MVC 解决方案:
其中一些可以组合,但将它们分开可以更轻松地定位内容并确保尊重层边界。
用于显示产品购物车的典型控制器操作可能如下所示:
用于将产品添加到购物车的典型控制器操作可能如下所示:
I typically structure my MVC solutions in a way resembling the following:
Some of these could be combined, but having them separate makes it easier to locate stuff and to ensure your layer boundaries are respected.
A typical controller action for showing the product cart might look like this:
A typical controller action for adding a product to the shopping cart might thus look something like this:
我还阅读了 Sanderson 的书的第一版,它非常棒 - 一种非常简单的方法来学习和开始使用 ASP.NET MVC。不幸的是,您无法直接从他书中的概念跳到编写大型可维护应用程序。最大的障碍之一是弄清楚将业务逻辑和其他位于 UI 和持久存储之间的关注点放在哪里(称为关注点分离或 SOC 的概念)。
如果您有兴趣,请考虑阅读领域驱动设计。我并不认为我完全了解它,但它是从 Sanderson 的示例应用程序到成功分离 UI 问题、业务逻辑和存储问题的良好过渡。
我的解决方案有一个单独的服务层。控制器与服务层通信(使用依赖注入 - Ninject)。服务层可以访问我的域对象/业务逻辑和我的存储库(NHibernate - 也与 Ninject 一起启动)。我的视图或控制器中的逻辑很少 - 控制器服务于协调器的目的,并且我努力使控制器操作尽可能精简。
我的领域层(实体、业务逻辑等)没有依赖项。它没有引用我的 Web 项目或我的存储库项目。它通常被称为 POCO 或普通旧 C#/CLR 对象。
编辑:我在您的评论之一中注意到您正在使用 EF。如果不使用称为 Code First 的东西(去年 8 月检查时处于社区技术预览状态),EF 就不支持 POCO。仅供参考。
I also read Sanderson's first version of his book and it was fantastic - a very easy way to pick up and start using ASP.NET MVC. Unfortunately, you can't jump straight from the concepts in his book to writing a large maintainable application. One of the biggest hurdles is figuring out where to put your business logic and other concerns that lie between the UI and persistent storage (a concept called Separation of Concerns or SOC).
If you are interested, consider reading up on Domain Driven Design. I won't suggest that I know it perfectly, but serves as a good transition from Sanderson's sample applications into something that successfully separates UI concerns, business logic, and storage concerns.
My solution has a separate service layer. The controllers communicate with the service layer (using Dependency Injection - Ninject). The service layer has access to my domain objects / business logic and my repositories (NHibernate - also spun up with Ninject). I have very little logic in my views or controllers - the controllers serve a purpose of coordinator and I strive to keep my controller actions as thin as possible.
My domain layer (entities, business logic, etc.) has no dependencies. It does not have references to my Web project or to my Repository project. It is what is often referred to as POCO or Plain Old C#/CLR Objects.
EDIT: I noticed in one of your comments you are using EF. EF does not support POCO without using something called Code First (was in Community Technology Preview status when I checked last August). Just FYI.
事实上,这个答案没有什么灵丹妙药,它本质上是一个上下文问题。我喜欢尽可能地将事物分开,如果你仔细想想,业务逻辑只是应用程序中的另一层。
我开发了一个受 BDD 启发的决策引擎,并将其作为可通过 NuGet 访问的 OSS 项目发布。该项目称为 NDecision,您可以在 NDecision 项目主页 上了解该项目。
NDecision 使决策树业务逻辑的实现变得非常简单,如果您是 Gherkin 语法和 Fluent 编码实践的粉丝,那么您会觉得使用它很自在。下面的代码快照来自项目站点,演示了如何实现业务逻辑。
这可能不是适合您的解决方案,但想法是,如果您已经在一个域模型中程序集 - 正如之前另一个答案中所建议的一个好主意和常见做法,没有理由不能用你的“决策树”进行另一个程序集。 NDecision 的编写就是考虑到这一点,将逻辑分离到一个独立的层中。
希望这会有所帮助。
The reality is that there's no silver bullet to this answer, and it really is a contextual question at heart. I like to separate things as much as possible, and business logic is, if you think about it, just another layer in an app.
I worked up a BDD-inspired decisioning engine and released it as an OSS project available via NuGet. The project is called NDecision, and you can read about it on the NDecision project home page.
NDecision makes decision-tree business logic quite simple to implement, and if you're a fan of Gherkin syntax and Fluent coding practices you'll feel right at home using it. The code snapshot below is from the project site, and demonstrates how business logic could be implemented.
This might not be a solution for you, but the idea is, if you already have your domain model in one assembly - a great idea and common practice as suggested in another answer earlier, there's no reason why you can't have another assembly with your "decision tree." NDecision was written with that in mind, to separate the logic into one independent layer.
Hopefully that'll be of some assistance.
如果您想要可扩展性,直接从 GUI 使用数据访问对象是不好的做法。所以在你提到的例子中,我认为你应该用一些支持业务操作的业务逻辑类来替换直接读取或写入DB的存储库。
If you want scalability, it's bad practice to use data access objects directly from the GUI. So in the example you mentioned, I think you should replace the repository that directly reads or writes to the DB by some business logic class that supports business operations.
在模型(MVC 中的 M)或领域层中。这些是一组单独的类型(最好在单独的项目中),代表域模型、服务和存储库。
In the model (M in MVC), or domain layer. These are a separate set of types (preferably in a separate project) that represent domain models, services and repositories.