服务层如何适合我的存储库实现?

时间:2011-05-26 12:43:48

标签: c# asp.net domain-driven-design repository repository-pattern

我创建了一个POCO模型类和一个处理持久性的存储库类。由于POCO无法访问存储库,因此存储库中有许多业务逻辑任务似乎不对。根据我的阅读,我看起来需要一个位于UI使用者和存储库层之间的服务层。我不确定的是它应该如何工作...

除了服务层,是否还应该有一个单独的业务逻辑层,或者是服务层的角色?

每个存储库应该有一个服务吗?

服务层是UI可以实例化模型对象还是存储库为服务提供新模型实例的唯一方式?

我是否将我的参数,模型和其他验证放在服务层中进行检查以确保输入有效以及更新前数据库中是否存在要更新的项目?

模型,存储库和UI都可以调用服务层,还是仅供UI使用?

服务层是否应该是所有静态方法?

从UI调用服务层的典型方法是什么?

模型与服务层应该有哪些验证?

以下是我现有图层的示例代码:

public class GiftCertificateModel
{
    public int GiftCerticiateId {get;set;}
    public string Code {get;set;}
    public decimal Amount {get;set;}
    public DateTime ExpirationDate {get;set;}

    public bool IsValidCode(){}
}


public class GiftCertificateRepository
{
    //only way to access database
    public GiftCertificateModel GetById(int GiftCertificateId) { }
    public List<GiftCertificateModel> GetMany() { }
    public void Save(GiftCertificateModel gc) { }
    public string GetNewUniqueCode() { //code has to be checked in db }

    public GiftCertificateModel CreateNew()
    {
        GiftCertificateModel gc = new GiftCertificateModel();
        gc.Code = GetNewUniqueCode();
        return gc;
    }              
}

更新 我目前正在使用Web表单和经典的ADO.NET。我希望最终转向MVC和EF4。

更新:非常感谢@Lester的出色解释。我现在明白我需要为每个存储库添加一个服务层。该层将是UI或其他服务可以与存储库通信的唯一方式,并且将包含任何不适合域对象的验证(例如 - 需要调用存储库的验证)

public class GiftCertificateService()
{

    public void Redeem(string code, decimal amount)
    {
        GiftCertificate gc = new GiftCertificate();
        if (!gc.IsValidCode(code))
        {
            throw new ArgumentException("Invalid code");
        }

        if (amount <= 0 || GetRemainingBalance(code) < amount)
        {
            throw new ArgumentException("Invalid amount");
        }

        GiftCertificateRepository gcRepo = new GiftCertificateRepository();
        gcRepo.Redeem(code, amount);
    }

    public decimal GetRemainingBalance(string code)
    {
        GiftCertificate gc = new GiftCertificate();            
        if (!gc.IsValidCode(code))
        {
            throw new ArgumentException("Invalid code");
        }

        GiftCertificateRepository gcRepo = new GiftCertificateRepository();
        gcRepo.GetRemainingBalance(code);
    }

    public SaveNewGC(GiftCertificate gc)
    {
        //validates the gc and calls the repo save method
        //updates the objects new db ID
    }

}

问题

  1. 我是否在服务上添加了相同(可能更多)的属性(金额,代码等),或者我只提供接受GiftCertificate对象和直接参数的方法?

  2. 我是否在调用Service构造函数时创建GiftCertificate实体的默认实例,或者根据需要创建新的实例(例如 - 对于需要在实体中调用验证方法的服务中的验证方法?另外,关于创建默认存储库实例的相同问题......?

  3. 我知道我通过服务公开了repo的功能,我是否也从实体中公开了方法(例如 - IsValidCode等)?

  4. UI可以直接创建新的GiftCertificate对象而无需通过服务(例如 - 从实体调用参数验证方法)。如果没有,如何执行它?

  5. 在UI图层上,当我想创建一个新的礼品券时,我是直接从UI层调用模型/服务验证(如IsValidExpirationDate等)还是首先保湿对象,然后通过它将被验证,然后将某种验证摘要返回给UI?

  6. 此外,如果我想从UI层兑换,我是否首先从UI调用模型/服务验证方法以提供用户反馈,然后调用Redeem方法,该方法将在内部再次运行相同的检查?

    调用服务从UI执行兑换操作的示例:

    string redeemCode = RedeemCodeTextBox.Text;
    
    GiftCertificateService gcService = new GiftCertificateService();
    GiftCertificate gc = new GiftCertificate(); //do this to call validation methods (should be through service somehow?)
    
    if (!gc.IsValid(redeemCode))
    {
        //give error back to user
    }
    
    if (gcService.GetRemainingBalance(redeemCode) < amount)
    {
        //give error back to user
    }
    
    //if no errors
    gcService.Redeem(code,amount);
    

    从UI创建新礼品券的示例:

    GiftCertificateService gcService = new GiftCertificateService();
    GiftCertificate gc = new GiftCertificate();
    
    if (!gc.IsValidExpDate(inputExpDate))
    {
        //give error to user..
    }
    
    //if no errors...
    gc.Code = gcService.GetNewCode();
    gc.Amount = 10M;
    gc.ExpirationDate = inputExpDate;
    gcService.SaveNewGC(gc);
    //method updates the gc with the new id...
    

    创建GC的方式和实体/服务之间的验证分离方式有些不对劲。用户/消费者不应该关心哪些验证在哪个地方......建议?

3 个答案:

答案 0 :(得分:42)

答案 1 :(得分:3)

  1. 您不必为每个实体创建存储库see here for more

      

    通常每个人定义一个存储库   在域中聚合。那就是:我们   每个实体没有存储库!如果   我们来看一个简单的订单输入   系统实体订单可能是   订单聚合的根。因此我们   将有一个订单存储库。

  2. 每个存储库应该有一个服务吗? - &GT;并非总是如此,因为您可以在一个服务中使用多个存储库。

  3. 服务创建模型实例,存储库永远不会与模型交互,实际上它返回模型将随后使用的实体。

  4. 在UI级别处理输入/范围等类型的验证(您可以使用javascript或任何其他库),并且让服务仅处理业务方面。您可以获得属性相同的好处。

  5. UI-&gt; Service-&gt;存储库,如果存储库调用服务而不是thr必须是错误的IMO。


  6. 您编码更改,

    1. 将模型和存储库分开。

      public class GiftCertificateModel
      {
      }
      public class GiftCertificateRepository
      {
         //Remove Model related code from here, and just put ONLY database specific code here, (no business logic also). Common methods would be Get, GetById, Insert, Update etc. 
      
          Since essence of Repository is to have common CRUD logic at one place soyou don't have to write entity specific code. 
          You will create entity specific repository in rare cases, also by deriving base repository.
      
      }
      
      public class GiftCertificateService()
      {
          //Create Model instance here
          // Use repository to fill the model (Mapper)
      
      }
      

答案 2 :(得分:0)

您可以创建名为GiftCertificateService的服务。

通过这种方式,您可以将任何不属于GiftCertificateModel职责的任务协调到其服务中。 (不要与WCF服务混淆)。

该服务将控制所有任务,因此您的UI(或任何可能的调用者)将使用服务中定义的方法。

然后,服务将调用模型上的方法,使用存储库,创建事务等。

对于前。 (根据您提供的示例代码):

public class GiftCertificateService 
{
   public void CreateCertificate() 
   {
      //Do whatever needs to create a certificate.
      GiftCertificateRepository gcRepo = new GiftCertificateRepository();
      GiftCertificateModel gc = gcRepo.CreateNew();
      gc.Amount = 10.00M;
      gc.ExpirationDate = DateTime.Today.AddMonths(12);
      gc.Notes = "Test GC";
      gcRepo.Save(gc);
   }
}

UI将调用CreateCertificate方法(传递参数等),该方法也可能返回一些内容。

注意:如果你希望类在UI上运行,那么创建一个控制器类(如果你正在做MVC)或者一个presenter类(如果你正在做MVVM,并且不想把所有内容放在ViewModel中并使用该类中的GiftCertificateService。