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

我创build了一个POCO模型类和一个处理持久性的库类。 由于POCO无法访问存储库,因此存储库中有许多业务逻辑任务看起来不正确。 从我所读到的,看起来像我需要一个位于用户界面和存储库层之间的服务层。 我不确定的是它应该如何工作…

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

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

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

我是否将参数,模型和其他validation放在服务层中进行检查,以确保input有效,并且在更新之前数据库中存在要更新的项目?

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

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

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

应该在模型和服务层上进行什么validation?

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

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或其他服务可以与存储库通信的唯一方式,并且将包含任何不适合域对象的validation(例如 – 需要调用回购的validation)

 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构造函数时创buildGiftCertificate实体的默认实例,还是仅根据需要创build新实例(例如 – 用于需要调用实体中的validation方法的服务中的validation方法?另外,关于创build默认存储库实例…?

  3. 我知道我通过服务公开了回购的function,我也暴露了实体的方法(例如 – IsValidCode等)?

  4. UI可以直接创build一个新的GiftCertificate对象而无需通过服务(例如 – 从实体中调用参数validation方法)。 如果没有,如何执行?

  5. 在UI层上,当我想要创build一个新的礼券时,是否直接从UI层调用模型/服务validation(如IsValidExpirationDate等),或者先保存对象,然后传入validation然后返回一些validation总结回到用户界面?

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

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

 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创build新礼券的示例:

 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... 

关于创buildGC的方式以及validation如何在实体/服务之间分开,感觉有些问题。 用户/消费者不应该关心什么validation是在哪个地方…build议?

看看S#arp Architeture 。 这就像构buildASP.NET MVC应用程序的最佳实践架构框架。 一般的体系结构模式是每个实体只有一个存储库,每个实体只负责数据访问,每个存储库只负责业务逻辑和控制器与服务之间的通信。

基于S#arp Architeture回答你的问题:

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

模型应负责现场级validation(例如使用必需的字段属性),而控制器可以在保存之前validation数据(例如,在保存之前检查状态)。

每个存储库是否应该有一个服务层?

是的 – 每个存储库应该有一个服务(每个存储库不是一个服务层,但我猜你的意思是)。

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

存储库和服务可根据需要返回单个实体,实体集合或数据传输对象(DTO)。 控制器会将这些值传递给模型中的静态构造函数方法,该方法将返回模型的一个实例。

例如使用DTO:

 GiftCertificateModel.CreateGiftCertificate(int GiftCerticiateId, string Code, decimal Amount, DateTime ExpirationDate) 

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

模型validation字段级别的值。 通过检查所需的字段,年龄或date范围等来确保input有效。服务应该做任何需要的validation,需要在模型外部进行检查。 检查礼品券是否还没有兑换,检查礼品券的商店属性)。

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

控制器和其他服务应该是唯一打电话给服务层的服务。 服务应该是唯一一个打电话给存储库。

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

他们可以但是如果不是,维护和扩展会更容易。 如果每个实体/子类有一个服务,则更改实体和添加/删除子类更容易更改。

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

控制器调用服务层的一些例子:

 giftCertificateService.GetEntity(giftCertificateId); (which in turn is just a call to the giftCertificateRepository.GetEntity(giftCertificateId) giftCertificateService.Redeem(giftCertificate); 

应该在模型和服务层上进行什么validation?

已经在上面回答了。

UPDATE

既然你使用的是WebForms,可能有点难以掌握一些概念,但是我所提到的一切都是适用的,因为我所描述的是一个通用的MVC模式。 用于数据访问的ADO.NET并不重要,因为数据访问是通过存储库解耦的。

添加相同(可能更多)的属性到我的模型(数量,代码等),或者我只提供接受GiftCertificate对象和直接参数的方法吗?

你需要把服务看作他们的名字所暗示的 – 控制器可以调用的动作。 您将不需要在模型中定义的属性,因为它们在模型中已经可用。

在调用Service构造函数时创buildGiftCertificate实体的默认实例,还是仅根据需要创build新实例(例如 – 用于需要调用实体中的validation方法的服务中的validation方法?另外,关于创build默认存储库实例…?

控制器和服务应分别具有专用于服务和存储库的字段。 你不应该为每个动作/方法实例化。

我知道我通过服务公开了回购的function,我也暴露了实体的方法(例如 – IsValidCode等)?

不太确定你在这里的意思。 如果服务返回实体,那么实体上的这些方法已经暴露。 如果他们返回DTO,那么这意味着你只对某些信息感兴趣。

对于validation,我可以看到为什么你有点担心,因为直接在模型上进行了validation,而在服务中完成了其他types的validation。 我使用的经验法则是,如果validation需要调用数据库,那么它应该在服务层完成。

UI可以直接创build一个新的GiftCertificate对象而无需通过服务(例如 – 从实体中调用参数validation方法)。 如果没有,如何执行?

在UI层上,当我想要创build一个新的礼券时,是否直接从UI层调用模型/服务validation(如IsValidExpirationDate等),或者先保存对象,然后传入validation然后返回一些validation总结回UI?

对于这两个问题,我们可以通过一个场景:

用户input信息以创build新证书并提交。 有字段级validation,所以如果一个文本框为空或如果美元金额是负面的,它会引发validation错误。 假设所有字段都是有效的,控制器将调用服务gcService.Save(gc)

该服务将检查其他业务逻辑,例如商店是否已经发行了太多的礼券。 如果有多个错误代码,它将返回状态的枚举值,或者抛出带有错误信息的exception。

最后,服务调用gcRepository.Save(gc)

  1. 你不必为每个实体创build仓库, 在这里看到更多 ,

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

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

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

  4. 处理input/范围等UItypes的validation(你可以使用JavaScript或任何其他库),并让服务只处理业务方面。 你可以得到属性的好处,这将是相同的。

  5. UI-> Service-> Repository,如果存储库正在调用服务比thr必须是错误的IMO。


你编码改变,

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

您可以创build一个名为GiftCertificateService的服务。

这样你就可以将任何不属于GiftCertificateModel责任的任务协调到它的服务中。 (不要与WCF服务混淆)。

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

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

例如。 (根据您提供的示例代码):

 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上工作,那么创build一个控制器类(如果你正在做MVC)或者一个演示者类(如果你正在做MVVM,并且不想把所有东西放在ViewModel中)该类的GiftCertificateService。