服务层和DAL层之间交互的设计问题

时间:2014-04-29 15:25:26

标签: c# asp.net-mvc ef-code-first onion-architecture

我的项目有一个设计问题,我不知道如何修复,我有一个DAL层,它包含存储库和一个包含“处理器”的服务层。处理器的作用是提供对DAL数据的访问并执行一些验证和格式化逻辑。

我的域对象都引用了服务层中的至少一个对象(从存储库中检索其属性的值)。但是我面临两个周期性的依赖。第一个“循环依赖”来自我的设计,因为我希望我的DAL返回域对象 - 我的意思是它是概念性的 - 第二个来自我的代码。

  1. 域对象始终至少依赖于一个服务对象
  2. 域对象通过调用服务上的方法
  3. 从存储库中检索其属性
  4. 服务方法调用DAL
  5. 但是 - 并且存在问题 - 当DAL完成他的工作时,他必须返回域对象。但是要创建这些对象,他必须注入所需的服务对象依赖项(因为域对象需要这些依赖项)。
  6. 因此,我的DAL存储库依赖于服务对象。
  7. 这导致了非常明显的周期依赖性。我对如何应对这种情况感到困惑。最后我考虑让我的DAL返回DTO但它似乎与洋葱架构不兼容。因为DTO是在基础架构中定义的,但核心和服务层不应该知道Infrastucture。

    enter image description here

    另外,由于我有数百行代码,因此我对改变存储库所有方法的返回类型并不感到兴奋......

    感谢任何帮助,谢谢!

    更新

    这是我的代码,使情况更加清晰:

    我的对象(在核心中):

    public class MyComplexClass1
    {
        MyComplexClass1 Property1 {get; set;}
        MyComplexClass2 Property2 {get; set;}
        private readonly IService MyService {get; set;}
    
        public MyComplexClass1(IService MyService)
        {
           this.MyService = MyService;
           this.Property1 = MyService.GetMyComplexClassList1();
           .....
        }
    

    这是我的服务界面(在核心中)

     public interface IService
     {
         MyComplexClass1 GetMyComplexClassList1();
         ...
     }
    

    这是我的存储库接口(在核心中)

    public interface IRepoComplexClass1
    {
         MyComplexClass1 GetMyComplexClassObject()
         ...
    }
    

    现在服务层实现了IService,DAL层实现了IRepoComplexClass1。

    但我的观点是,在我的回购中,我需要构建我的域对象

    这是Infrascruture Layer

    using Core;
    
    public Repo : IRepoComplexClass1
    {
       MyComplexClass1 GetMyComplexClassList1()
       {
           //Retrieve all the stuff...
           //... And now it's time to convert the DTOs to Domain Objects
           //I need to write 
    
           //DomainObject.Property1 = new MyComplexClass1(ID, Service);
           //So my Repository has a dependency with my service and my service has a dependency with my repository, (Because my Service Methods, make use of the Repository). Then, Ninject is completely messed up.
       }
    

    我希望现在更清楚了。

1 个答案:

答案 0 :(得分:3)

首先,通常,Onion ArchitectureDomain Driven Design(DDD)等架构指南在设计系统时并不适合所有情况。实际上,不鼓励使用这些技术,除非域具有显着的复杂性以保证成本。因此,您正在建模的域很复杂,不适合更简单的模式。

恕我直言,洋葱架构和DDD都试图达到同样的目的。也就是说,具有可编程(并且可能是易于移植的)域的能力,用于复杂逻辑,而没有所有其他问题。这就是为什么在Onion中,应用程序,基础架构,配置和持久性问题都处于边缘。

因此,总而言之,域只是代码。然后,它可以利用那些很酷的设计模式来解决手头的复杂问题,而不必担心任何其他问题。

我非常喜欢Onion文章,因为同心障碍的图片与分层架构的想法不同。

在分层架构中,通过图层可以轻松地纵向,上下思考。例如,您在顶部有一个服务(通过DTO或ViewModels),然后服务调用业务逻辑,最后,业务logic 调用一些持久层来保持系统状态。

然而,洋葱架构描述了一种不同的思考方式。您可能仍然在顶部有服务,但这是应用程序服务。例如,ASP.NET MVC中的Controller了解HTTP,应用程序配置设置和安全会话。但是控制器的工作并不仅仅是将工作推迟到更低(更智能)的层。我们的工作是尽快将应用程序方面映射到方面。简单地说,Controller调用域,要求执行一段复杂的逻辑,得到结果,然后继续。控制器是将事物放在一起的粘合剂(不是域)。

因此,域名是业务域的中心。没有别的。

这就是为什么有人抱怨需要域实体属性的ORM工具。我们希望我们的域名完全清除除手头问题之外的所有问题。所以,普通的老物件。

因此,域名不直接与应用程序服务存储库对话。事实上,域调用可以说明这些事情。域是核心,因此是执行堆栈的结束。

因此,对于一个非常简单的代码示例(改编自OP):

<强>存储库:

 // it is only infrastructure if it doesn't know about specific types directly
    public Repository<T>
    {
       public T Find(int id)
       {
           // resolve the entity
           return default(T);   
       }
    }

域名实体:

public class MyComplexClass1
{
    MyComplexClass1 Property1 {get; }  // requred because cannot be set from outside
    MyComplexClass2 Property2 {get; set;}
    private readonly IService MyService {get; set;}

    // no dependency injection frameworks!
    public MyComplexClass1(MyComplexClass1 property1)
    {
       // actually using the constructor to define the required properties
       // MyComplexClass1 is required and MyComplexClass2 is optional
       this.Property1 = property1;
       .....
    }

    public ComplexCalculationResult CrazyComplexCalculation(MyComplexClass3 complexity)
    {
       var theAnswer = 42;
       return new ComplexCalculationResult(theAnswer);
    }
}

控制器(应用服务):

public class TheController : Controller
{
    private readonly IRepository<MyComplexClass1> complexClassRepository;
    private readonly IRepository<ComplexResult> complexResultRepository;

    // this can use IoC if needed, no probs
    public TheController(IRepository<MyComplexClass1> complexClassRepository, IRepository<ComplexResult> complexResultRepository)
    {
        this.complexClassRepository = complexClassRepository;
        this.complexResultRepository = complexResultRepository;
    }

    // I know about HTTP
    public void Post(int id, int value)
    {
        var entity = this.complexClassRepository.Find(id);

        var complex3 = new MyComplexClass3(value);
        var result = entity.CrazyComplexCalculation(complex3);

        this.complexResultRepository.Save(result);  
    }
}

现在,很快你会想到,&#34;哇,控制器做得太多了#34;。例如,如果我们需要50个值来构造MyComplexClass3,那该怎么办?这就是洋葱建筑的辉煌。有一个名为FactoryBuilder的设计模式,没有应用程序问题或持久性问题的限制,您可以轻松实现它。因此,您将这些模式重构到域中(并且它们将成为您的域服务)。

总之,域调用都不了解应用程序或持久性问题。它是系统的核心。

希望这是有道理的,我写的比我想的要多一点。 :)