存储库和服务,MVC 模型

发布于 2024-10-14 19:53:33 字数 2113 浏览 0 评论 0原文

所以我一直在学习Repository模型,似乎预计Repositories不会做很多复杂的逻辑。不过,我还了解到,大多数业务逻辑不应该位于我的控制器内部。那么我该把它放在哪里呢?

我查看了一些示例应用程序,似乎它们有另一个层,称为Services,可以为事物执行更复杂的逻辑。那么这个因素是如何融入 MVC 模式的呢?

我是否想要构建我的服务来访问我的存储库,然后构建我的控制器来访问我的服务?像这样?

interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}
interface IMembershipRepository
{
 MembershipCreateStatus Create(string username, string password);
}

class MembershipRepository : IMembershipRepository
{
 public MembershipRepository(ISession session)
 {
  **// this is where I am confused...** 
 }
}
class MembershipService : IMembershipService
{
 private readonly IMembershipRepository membershipRepository;
 public MembershipService(IMembershipRepository membershipRepository)
 {
  this.membershipRepository = membershipRepository;
 }

 public bool ValidateUser(string username, string password)
 {
   // validation logic
 }
 public MembershipCreateStatus Create(string username, string password)
 {
  return membershipRepository.Create(username, password);
 }
}

class MembershipController : Controller
{
 private readonly IMembershipService membershipService;

 public MembershipController(IMembershipService membershipService)
 {
  this.membershipService = membershipService
 }
}

我的代码中标记的部分让我感到困惑。我读到的所有内容都表明我应该将我的 ISession 注入到我的存储库中。这意味着我无法将 ISession 注入到我的服务中,那么如何从我的服务中进行数据库访问呢?我不明白这里的适当流程是什么。

当我将 ValidateUser 放入我的 IMembershipRepository 时,我被告知这是“不好的”。但IMembershipRepository 是数据库访问所在的位置。这就是意图,对吗?保持数据库访问量非常少?但如果我不能在其中加入其他逻辑,那还有什么意义呢?

有人可以阐明这一点,并向我展示一个可能更可行的例子吗?

我正在使用 Fluent nHibernate、ASP.NET MVC 3.0 和 Castle.Windsor。

我是否应该做类似的事情......

class MembershipService
{
 private readonly IMembershipRepository membershipRepository;

 public MembershipService(ISession session)
 {
  membershipRepository = new MembershipRepository(session);
 }
}

并且永远不要让我的控制器直接访问存储库

So I've been learning about the Repository model, and it seems that it is expected that Repositories do not do a lot of intricate logic. However I also read that most of the business logic should not be inside of my Controllers. So where do I put it?

I've looked at some sample applications and it seems that they have another layer called Services that do more intricate logic for things. So how does this factor into the MVC pattern?

Do I want to build my services to access my repositories, and then my controllers to access my services? Like this?

interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}
interface IMembershipRepository
{
 MembershipCreateStatus Create(string username, string password);
}

class MembershipRepository : IMembershipRepository
{
 public MembershipRepository(ISession session)
 {
  **// this is where I am confused...** 
 }
}
class MembershipService : IMembershipService
{
 private readonly IMembershipRepository membershipRepository;
 public MembershipService(IMembershipRepository membershipRepository)
 {
  this.membershipRepository = membershipRepository;
 }

 public bool ValidateUser(string username, string password)
 {
   // validation logic
 }
 public MembershipCreateStatus Create(string username, string password)
 {
  return membershipRepository.Create(username, password);
 }
}

class MembershipController : Controller
{
 private readonly IMembershipService membershipService;

 public MembershipController(IMembershipService membershipService)
 {
  this.membershipService = membershipService
 }
}

The marked part of my code is what confuses me. Everything I have read said I should be injecting my ISession into my repositories. This means I could not be injecting ISession into my services, so then how do I do Database access from my Services? I'm not understanding what the appropriate process is here.

When I put ValidateUser in my IMembershipRepository, I was told that was 'bad'. But the IMembershipRepository is where the database access resides. That's the intention, right? To keep the database access very minimal? But if I can't put other logic in them, then what is the point?

Can someone shed some light on this, and show me an example that might be more viable?

I am using Fluent nHibernate, ASP.NET MVC 3.0, and Castle.Windsor.

Should I instead do something like ...

class MembershipService
{
 private readonly IMembershipRepository membershipRepository;

 public MembershipService(ISession session)
 {
  membershipRepository = new MembershipRepository(session);
 }
}

And never give my Controllers direct access to the Repositories?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终弃我 2024-10-21 19:53:33

我读到的所有内容都说我应该将 ISession 注入到我的存储库中。

这是正确的。您需要将会话注入存储库构造函数,因为这是进行数据访问的地方。

这意味着我无法将 ISession 注入到我的服务中,那么如何从我的服务中进行数据库访问?

您不在服务中进行数据库访问。该服务依赖于注入到其构造函数中的一个或多个存储库并使用它们各自的方法。该服务从不直接查询数据库。

回顾一下:

  • 存储库包含模型上的简单 CRUD 操作。这是执行数据访问的地方。这种数据访问不一定意味着数据库。这将取决于您使用的底层存储。例如,您可以调用云上的某些远程服务来执行数据访问。
  • 该服务依赖一个或多个存储库来实现业务操作。此业务操作可能依赖于存储库上的一个或多个 CRUD 操作。服务甚至不应该知道数据库的存在。
  • 控制器使用服务来调用业务操作。
  • 为了减少不同层之间的耦合,使用接口来抽象操作。

Everything I have read said I should be injecting my ISession into my repositories.

That's correct. You need to inject the session into the repository constructor because this is where the data access is made.

This means I could not be injecting ISession into my services, so then how do I do Database access from my Services?

You don't do database access in your services. The service relies on one or more repositories injected into its constructor and uses their respective methods. The service never directly queries the database.

So to recap:

  • The repository contains the simple CRUD operations on your model. This is where the data access is performed. This data access doesn't necessary mean database. It will depend on the underlying storage you are using. For example you could be calling some remote services on the cloud to perform the data access.
  • The service relies on one or more repositories to implement a business operation. This business operation might depend on one or more CRUD operations on the repositories. A service shouldn't even know about the existence of a database.
  • The controller uses the service to invoke the business operation.
  • In order to decrease the coupling between the different layers, interfaces are used to abstract the operations.
剪不断理还乱 2024-10-21 19:53:33
interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}

创建这样的服务是一种反模式。

这样的服务有多少责任?有多少理由必须改变?

另外,如果你将逻辑放入服务中,你最终会得到一个贫乏的领域。您最终将得到事务脚本样式的过程代码。我并不是说这一定是坏事。

也许丰富的领域模型不适合您,但这应该是两者之间有意识的决定,并且这种多重责任服务在任何一种情况下都不合适。

这应该是一个巨大危险信号:

public MembershipCreateStatus Create(string username, string password)
{
  return membershipRepository.Create(username, password);
}

有什么意义?为了层而层?该服务不会增加任何价值,也没有任何作用。

有很多概念缺失。

首先,考虑使用工厂来创建对象:

public interface IMembershipFactory {
    MembershipCreateStatus Create(string username, string password);
}

工厂可以封装构建实例或开始实体对象生命周期的任何逻辑。

其次,存储库是对象集合的抽象。使用工厂创建对象后,将其添加到对象集合中。

var result = _membershipFactory.Create("user", "pw");
if (result.Failed); // do stuff
_membershipRepository.Add(status.NewMembership);  // assumes your status includes the newly created object

最后,MyEntityService 类包含可以在实体上执行的每个操作的方法,这对我来说似乎非常令人反感。

相反,我尝试通过将每个操作建模为单独的命令类而不是单个服务类上的方法来更明确并更好地捕获意图。

public class ChangePasswordCommand {
    public Guid MembershipId { get; set; }
    public string CurrentPassword { get; set; }
    public string NewPassword { get; set; }
}

然后,当发送此命令时,必须执行某些操作,因此我们使用处理程序:

public interface IHandle<TMessageType> {
    void Execute(TMessageType message);
}

public class ChangePasswordCommandHandler : IHandle<ChangePasswordCommand> {

    public ChangePasswordCommandHandler(
         IMembershipRepository repo
      ) 
      {}

    public void Execute(ChangePasswordCommand command) {
      var membership = repo.Get(command.MembershipId);
      membership.ChangePassword(command.NewPassword);
    }
}

使用与 IoC 容器交互的简单类来分派命令。

这有助于避免单一的服务类,并使项目的结构和逻辑位置更加清晰。

interface IMembershipService
{
 bool ValidateUser(string username, string password);
 MembershipCreateStatus Create(string username, string password);
}

Creating a service like this an anti-pattern.

How many responsibilities does a service like this have? How many reasons could it have to change?

Also, if you put your logic into services, you are going to end up with an anemic domain. What you will end up with is procedural code in a Transaction Script style. And I am not saying this is necessarily bad.

Perhaps a rich domain model is not appropriate for you, but it should be a conscious decision between the two, and this multiple responsibility service is not appropriate in either case.

This should be a HUGE red flag:

public MembershipCreateStatus Create(string username, string password)
{
  return membershipRepository.Create(username, password);
}

What is the point? Layers for the sake of layers? The Service adds no value here, serves no purpose.

There are a lot of concepts missing.

First, consider using a Factory for creating objects:

public interface IMembershipFactory {
    MembershipCreateStatus Create(string username, string password);
}

The factory can encapsulate any logic that goes into building an instance or beginning the lifetime of an entity object.

Second, Repositories are an abstraction of a collection of objects. Once you've used a factory to create an object, add it to the collection of objects.

var result = _membershipFactory.Create("user", "pw");
if (result.Failed); // do stuff
_membershipRepository.Add(status.NewMembership);  // assumes your status includes the newly created object

Lastly, MyEntityService class that contains a method for every operation that can be performed on an Entity just seems terribly offensive to my senses.

Instead, I try to be more explicit and better capture intent by modeling each operation not as a method on a single Service class, but as individual Command classes.

public class ChangePasswordCommand {
    public Guid MembershipId { get; set; }
    public string CurrentPassword { get; set; }
    public string NewPassword { get; set; }
}

Then, something has to actually do something when this command is sent, so we use handlers:

public interface IHandle<TMessageType> {
    void Execute(TMessageType message);
}

public class ChangePasswordCommandHandler : IHandle<ChangePasswordCommand> {

    public ChangePasswordCommandHandler(
         IMembershipRepository repo
      ) 
      {}

    public void Execute(ChangePasswordCommand command) {
      var membership = repo.Get(command.MembershipId);
      membership.ChangePassword(command.NewPassword);
    }
}

Commands are dispatched using a simple class that interfaces with our IoC container.

This helps avoids monolithic service classes and makes a project's structure and location of logic much clearer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文