代码之家  ›  专栏  ›  技术社区  ›  Craig Broadman

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

  •  3
  • Craig Broadman  · 技术社区  · 6 年前

    我有一个带后期操作的api。我们使用C#,每一层都有单独的组件(Api、业务逻辑、数据访问)

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

    1. 检查数据源1中是否已存在该数据
    2. 如果数据源1中不存在该数据,请检查数据源2中是否已经存在该数据
    3. 如果数据源2中不存在,请将其插入数据源1,然后再插入数据源2

    我们经常使用服务(用于逻辑)和存储库进行数据访问。

    选项1

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

    其优点是,所有的逻辑都在服务中,感觉更“单一责任”。这样做的缺点是,以后有人可以直接使用“datasource 1存储库”,并在不知道也应该将记录插入datasource 2的情况下插入记录。

    选择2 使用与两个数据源交互的单个get和insert操作创建一个存储库。这将确保你不能与一个没有另一个的人互动,但感觉更不像是“单一责任”。

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

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

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

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

    4 回复  |  直到 6 年前
        1
  •  0
  •   Community ahmed    4 年前

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

    其优点是,所有的逻辑都在服务中,感觉更“单一责任”。

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

    另一种选择是创建一个存储库,使用一个与两个数据源交互的插入操作。这将确保你不能与一个没有另一个的人互动,但感觉更不像是“单一责任”。

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

    另一个选择是拥有3个存储库。1是公共的,由服务(DDD)使用,该存储库只与内部的特定于数据源的存储库对话。

    我不知道你所说的“内部”是什么意思。如果你指的是内部 internal 与在程序集中一样,它仍然有与前一个选项相同的缺点。

    关于我和你都喜欢的第一种选择,你说:

    这样做的缺点是,有人很容易在以后出现,直接使用“datasource 1存储库”,插入一条记录,而不知道它也应该插入datasource 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 作为论据。

    好啊那么让我们使用回购协议:

    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());
    
        2
  •  0
  •   gmn    6 年前

    这个要求感觉很像 Chain of Responsibility Pattern 不幸的是,这并不能减少数据访问代码的数量。我很想在检查中每个“级别”都有一个读卡器,让它们都实现一些公共接口,并使用一个责任链(即,如果不要求链中的下一个对象,我可以处理这个问题)来执行操作。您可以将链中的每个项目都设置为迷你存储库,或者在需要时使用另一个存储库。

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

        3
  •  0
  •   Kirill Korolev    6 年前

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

    enter image description here

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

    enter image description here

    并且只有一个有效的数据源。

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

        4
  •  0
  •   Constantin Galbenu    6 年前

    从我看到的界面是这样的 persistSomethingToRepository .这个接口是客户端代码所关心的全部。它不关心您是否使用了辅助遗留系统(也不应该)。由于可以使用 Decorator pattern 用数据源2的持久化来装饰数据源1。

    我还没有足够的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 而不是 Source2和Source1 ,无需对客户端代码进行任何修改。这会给你一个 SOLID 代码,更确切地说是 Open/closed Principle 还有 Single responsibility principle .