寻找一种设计模式来管理需要更新多个数据源中的数据的操作
Look for a design pattern for managing an operation that needs to update data in multiple datasources
我有一个 api 和 post 操作。我们正在使用 C#,并且每一层都有单独的程序集(Api、业务逻辑、数据访问)
此操作必须与多个数据源交互。
- 检查数据源1中是否已经存在数据
- 如果数据源1中不存在,检查数据源2中是否已经存在
- 如果数据源2中不存在,则将其插入到数据源1中,然后再插入到数据源2中
我们经常使用服务(用于逻辑)和存储库进行数据访问。
选项 1
- 为每个数据源创建一个存储库,每个数据源都有一个获取操作和一个插入操作。
- 创建一个使用两个存储库并遵循上述逻辑的服务。
优点是所有的逻辑都在服务里面,感觉多了一点"Single Responsibility"。这样做的缺点是以后有人可以直接使用 "datasource 1 repository" 并插入一条记录,而不知道它也应该插入到数据源 2 中。
选项 2
使用与两个数据源交互的单个获取和插入操作创建一个单一的存储库。这将确保您无法在没有另一个的情况下与其中一个进行交互,但感觉不如 "Single Responsibility".
选项 3
拥有 3 个存储库....
- 1 个数据源 1 的存储库(但只有一个内部接口,因此不能被另一个程序集使用)
- 1 个数据源 2 的存储库(但只有一个内部接口,因此不能被另一个程序集使用)
- 1 个具有 public 接口的存储库,可以被服务使用(在不同的程序集中)
public 存储库可以使用 2 个 "datasource specific" 存储库并遵循上述逻辑。这将逻辑从服务转移到存储库,但优点是实现对数据层之外的任何人都是隐藏的。
人们的想法是什么?有这方面的设计模式吗?
这个要求感觉很像 Chain of Responsibility Pattern,不幸的是,这对减少数据访问代码量没有多大帮助。我很想在检查中每个 'level' 有一个 reader,让它们都实现一些通用接口,并使用责任链(即我可以处理这个,如果不问下一个对象在链中)进行操作。您可以使链中的每个项目成为一个小型存储库,或者如果需要使用另一个存储库。
如果结构中已有特定的存储库,或者在其他地方需要类似的 reader,也许只需要担心它们。
我为这些愚蠢的图画道歉,但我试图将您的工作流程表示为有限自动机。
所以,我想我们可以这样减少它。
并且只有一个有效的数据源。
此外,如果需要,请考虑同步机制,这可能会导致开销。
On one hand, I could create a repository for each datasource, each with a get operation and an insert operation. Then I could create a service which follows the logic above.
The advantage is that all the logic is in the service and it feels a little more "Single Responsibility".
我也喜欢这个选项,所以如果我是你,我会选择这个选项。
Another option is to create a single repository, with a single insert operation that interacts with both datasources. This would ensure that you cannot interact with one without the other but feels much less as "Single Responsibility".
我不喜欢这种将数据源检查放在一个存储库中的选项,因为这听起来像是服务层的责任。
Another option is to have 3 repositories. 1 that is public, consumed by the service (DDD) and this repository talks to the datasource specific repositories that are internal only.
我不确定您所说的仅限内部是什么意思。如果在内部你的意思是 internal
就像在程序集内一样,它仍然有与前一个选项相同的缺点。
关于第一个选项,我和你都喜欢,你这样说:
The downside to this is someone could easily come along at a later date and use the "datasource 1 repository" directly and insert a record without knowing that it should be inserted in to datasource 2 as well.
我们可以做一些事情来避免这种情况。像这样创建一个 class 并想象我们正在保存 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());
据我所知,界面类似于 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) 配置为在需要 Repository
时使用 Source2AndSource1
的实例。
当您的遗留系统(来源 2)不再使用时,您只需将 DIC 配置为使用 Source1
的实例而不是 Source2AndSource1
,而无需对客户端代码进行任何修改。这会让你得到 SOLID code, more exactly the Open/closed Principle and the Single responsibility principle.
我有一个 api 和 post 操作。我们正在使用 C#,并且每一层都有单独的程序集(Api、业务逻辑、数据访问)
此操作必须与多个数据源交互。
- 检查数据源1中是否已经存在数据
- 如果数据源1中不存在,检查数据源2中是否已经存在
- 如果数据源2中不存在,则将其插入到数据源1中,然后再插入到数据源2中
我们经常使用服务(用于逻辑)和存储库进行数据访问。
选项 1
- 为每个数据源创建一个存储库,每个数据源都有一个获取操作和一个插入操作。
- 创建一个使用两个存储库并遵循上述逻辑的服务。
优点是所有的逻辑都在服务里面,感觉多了一点"Single Responsibility"。这样做的缺点是以后有人可以直接使用 "datasource 1 repository" 并插入一条记录,而不知道它也应该插入到数据源 2 中。
选项 2 使用与两个数据源交互的单个获取和插入操作创建一个单一的存储库。这将确保您无法在没有另一个的情况下与其中一个进行交互,但感觉不如 "Single Responsibility".
选项 3 拥有 3 个存储库....
- 1 个数据源 1 的存储库(但只有一个内部接口,因此不能被另一个程序集使用)
- 1 个数据源 2 的存储库(但只有一个内部接口,因此不能被另一个程序集使用)
- 1 个具有 public 接口的存储库,可以被服务使用(在不同的程序集中)
public 存储库可以使用 2 个 "datasource specific" 存储库并遵循上述逻辑。这将逻辑从服务转移到存储库,但优点是实现对数据层之外的任何人都是隐藏的。
人们的想法是什么?有这方面的设计模式吗?
这个要求感觉很像 Chain of Responsibility Pattern,不幸的是,这对减少数据访问代码量没有多大帮助。我很想在检查中每个 'level' 有一个 reader,让它们都实现一些通用接口,并使用责任链(即我可以处理这个,如果不问下一个对象在链中)进行操作。您可以使链中的每个项目成为一个小型存储库,或者如果需要使用另一个存储库。
如果结构中已有特定的存储库,或者在其他地方需要类似的 reader,也许只需要担心它们。
我为这些愚蠢的图画道歉,但我试图将您的工作流程表示为有限自动机。
所以,我想我们可以这样减少它。
并且只有一个有效的数据源。
此外,如果需要,请考虑同步机制,这可能会导致开销。
On one hand, I could create a repository for each datasource, each with a get operation and an insert operation. Then I could create a service which follows the logic above.
The advantage is that all the logic is in the service and it feels a little more "Single Responsibility".
我也喜欢这个选项,所以如果我是你,我会选择这个选项。
Another option is to create a single repository, with a single insert operation that interacts with both datasources. This would ensure that you cannot interact with one without the other but feels much less as "Single Responsibility".
我不喜欢这种将数据源检查放在一个存储库中的选项,因为这听起来像是服务层的责任。
Another option is to have 3 repositories. 1 that is public, consumed by the service (DDD) and this repository talks to the datasource specific repositories that are internal only.
我不确定您所说的仅限内部是什么意思。如果在内部你的意思是 internal
就像在程序集内一样,它仍然有与前一个选项相同的缺点。
关于第一个选项,我和你都喜欢,你这样说:
The downside to this is someone could easily come along at a later date and use the "datasource 1 repository" directly and insert a record without knowing that it should be inserted in to datasource 2 as well.
我们可以做一些事情来避免这种情况。像这样创建一个 class 并想象我们正在保存 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());
据我所知,界面类似于 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) 配置为在需要 Repository
时使用 Source2AndSource1
的实例。
当您的遗留系统(来源 2)不再使用时,您只需将 DIC 配置为使用 Source1
的实例而不是 Source2AndSource1
,而无需对客户端代码进行任何修改。这会让你得到 SOLID code, more exactly the Open/closed Principle and the Single responsibility principle.