寻找用于管理需要更新多个数据源中的数据的操作的设计模式

时间:2018-05-18 20:42:46

标签: c# design-patterns domain-driven-design repository-pattern solid-principles

我有一个后期操作api。我们正在使用C#,并为每个层(Api,业务逻辑,数据访问)提供单独的程序集

此操作必须与多个数据源进行交互。

  1. 检查数据源1中是否已存在数据
  2. 如果数据源1中不存在,请检查数据源2中是否已存在数据
  3. 如果数据源2中不存在,请将其插入数据源1,然后将其插入数据源2
  4. 我们经常使用服务(用于逻辑)和存储库进行数据访问。

    选项1

    • 为每个数据源创建一个存储库,每个数据源都有一个get操作和一个插入操作。
    • 创建一个消耗两个存储库并遵循上述逻辑的服务。

    优势在于所有逻辑都在服务中,并且感觉更加“单一责任”。这样做的缺点是,以后有人可以直接使用“datasource 1 repository”并插入记录,而不知道它也应该插入到数据源2中。

    选项2 创建一个单独的存储库,使用一个与两个数据源交互的get和insert操作。这样可以确保您不会在没有其他人的情况下与其他人互动,但会感觉不到“单一责任”。

    选项3 有3个存储库......

    • 1个数据源1的存储库(但是只有一个内部接口,因此无法被其他程序集使用)
    • 数据源2的1个存储库(但是只有一个内部接口,因此无法被另一个程序集使用)
    • 1个具有公共接口的存储库,可以被服务使用(在不同的程序集中)

    公共存储库可以使用2个“特定于数据源”的存储库并遵循上面的逻辑。这将逻辑从服务转移到存储库,但具有以下优点:实现对数据层之外的任何人都是隐藏的。

    人们的想法是什么?这有设计模式吗?

4 个答案:

答案 0 :(得分:0)

这个要求感觉很像Chain of Responsibility Pattern,遗憾的是,它不会为减少数据访问代码量做很多事情。我很想读一个符合读者水平的读者。在检查中,让它们都实现一些通用的接口,并使用一个责任链(即我可以处理这个,如果没有请求链中的下一个对象)来执行操作。您可以将链中的每个项目设为迷你存储库,或者根据需要使用其他存储库。

如果你已经在结构中拥有它们,或者在其他地方需要类似的读者,可能只会担心特定的存储库。

答案 1 :(得分:0)

我为这些愚蠢的图画道歉,但我试图将您的工作流程表示为有限自动机。

enter image description here

所以,我想我们可以像这样减少它。

enter image description here

只有一个有效的数据源。

另外,如果需要,请考虑同步机制,这可能会导致开销。

答案 2 :(得分:0)

  

一方面,我可以为每个数据源创建一个存储库,每个数据源都有一个get操作和一个insert操作。然后我可以创建一个遵循上述逻辑的服务。

     

优势在于所有逻辑都在服务中,并且感觉更加“单一责任”。

我也喜欢这个选项,如果我是你,我会选择这个选项。

  

另一种选择是创建单个存储库,使用单个插入操作与两个数据源进行交互。这样可以确保您不会在没有其他人的情况下与其他人互动,但会感觉不到“单一责任”。

我不喜欢将数据源检查放在一个仓库中的选项,因为这听起来像是服务层的责任。

  

另一种选择是拥有3个存储库。 1是公共的,由服务(DDD)使用,并且此存储库与仅内部的数据源特定存储库进行通信。

我不确定你的内心是什么意思。如果在内部你的意思是internal,就像在程序集中一样,它仍然有与前一个选项相同的缺点。

关于第一个选项,我和你喜欢,你这样说:

  

这样做的缺点是有人可以在以后轻松地使用“数据源1存储库”直接插入记录而不知道它应该插入到数据源2中。

我们可以做些什么来避免这种情况。创建一个这样的类,并想象我们正在保存Customer

public class Customer { }

public class CustomerData
{
    public string MyProperty { get; private set; }

    private CustomerData()
    {
    }

    public Customer Customer { get; private set; }

    // or not void
    public static void Save(Customer customer)
    {
        // Check all datasources and save wherever you need to 
        // obviously through injection or whatever. I will just
        // new-it up here
        var cd = new CustomerData { Customer = customer };
        var repo1 = new CustomerRepository();
        repo1.Save(cd);

        bool someCondition = true;
        if (someCondition)
        {
            var repo2 = new CustomerRepository();
            repo2.Save(cd);
        }
    }
}

请注意构造函数是私有的,因此开发人员无法创建它。现在像这样创建您的存储库:

public class CustomerRepository
{
    public void Save(CustomerData cd) { // Save cd.Customer to datasource }
}

请注意,它需要CustomerData作为参数。

OK!所以让我们使用repo:

var repo = new CustomerRepository();
// Oops cannot create CustomerData. 
// repo has no other method except for CustomerData.
repo.Save(new CustomerData()); 

// There is no other way except for this or the developer has to add 
// a method to the repo-but what can we do about that, not much!
CustomerData.Save(new Customer());

答案 3 :(得分:0)

从我所看到的界面就像persistSomethingToRepository。此接口是所有客户端代码关心的。它并不关心您使用辅助遗留系统的事实(也不应该)。这种观察很大程度上简化了实现,因为您可以使用Decorator pattern来装饰DataSource1,同时保持对DataSource2的持久性。

我没有足够的C#经验,但伪OOP代码是这样的:

interface Repository {
    void persistSomething(something);
}

class Source1 implements Repository {
    void persistSomething(something) {
        // insert into database
   }
}

class Source2AndSource1 implements Repository {
    // inject the right database connection and a Source1 instance
    void persistSomething(something) {
        // Check if the data already exists in datasource 1
        // If it doesn't exist in datasource 1, check if the data already exists in datasource  2
       //If it doesn't exist in datasource 2, insert it in to datasource 1 and then insert it in to datasource 2
   }
}

然后,只需配置您的依赖注入容器(DIC),以便在需要Source2AndSource1时使用Repository的实例。

当您的旧系统(源2)不再使用时,您只需将DIC配置为使用Source1而不是Source2AndSource1的实例,而无需对客户端代码进行任何修改。这会为您提供SOLID代码,更准确地说是Open/closed PrincipleSingle responsibility principle