如何以正确的方式实现存储库模式?
How to implement the repository pattern the right way?
在为我的 ASP.NET 项目实现存储库模式时,我遇到了一些无法解决的问题。所以我有几个关于如何正确实现存储库模式的问题。
根据我的经验,我认为 classes/models 仅在我的应用程序中没有行为,紧挨着他们的存储库不是好的 OOP。但是,这就是我实现存储库模式的方式。我只是在任何需要存储库实例的地方制作,以执行一些操作。这种方法的结果是我的所有域 classes 都没有行为。
它们只是没有方法的保存数据的对象。我的老师对我说,我用的是瘦模型,我应该努力做胖模型。
作为对该反馈的回应,我在 classes 中实现了一些业务逻辑,但我 运行 遇到了一些问题:
场景:
我的 User
class 有一个朋友列表,里面有用户对象,代表某个用户的朋友。向用户添加新朋友时,域 class 方法会检查 "friend" 是否已存在于朋友列表中。如果没有,他将被添加到列表中。这些更改需要发送到数据库以进行持久化。
我知道这必须在每个域的存储库中完成 class,但是对存储库方法的调用属于应用程序架构中的什么地方?
现在我在域 class 方法本身中调用存储库方法,以持久保存对数据库的更改:
public void AddFriend(User friend)
{
foreach(User f in Friends)
{
if(f.Username == friend.Username)
{
throw new Exception(String.Format("{0} is already a friend.", friend.Username));
}
}
Friends.Add(friend);
userRepo.AddFriend(this.Id, friend.Id);
}
这是一个好方法吗,出于某种原因我认为它不是。关于单元测试,我们需要使用这种方法进行一些依赖注入,这对我来说意味着它不是一个独立的 class(良好的可测试单元)。我读过一些人的帖子,说他们使用额外的服务层或其他东西。我认为这里需要这种抽象,但是某个服务层是什么样的?里面有什么,有哪些方法等?
我看到其他一些学生在域中使用静态方法class,它提供了添加新对象、更新对象、删除对象和获取所有对象等功能。
示例:
public class Tram
{
private static TramRepository Repo = new TramRepository(new DBTram());
public static void AddTram(int tramID, TramType type, int lineNr)
{
Tram tram = new Tram(tramID, type, TramStatus.depot, lineNr, true, null);
Repo.AddTram(tram);
}
public static List<Tram> GetAll()
{
Repo.GetAll();
}
}
我发现在域 class 中向数据库添加新实体的方法很奇怪,这就是该实体本身。同样对于 GetAll() 方法,我认为在 class 本身中有一个获取所有电车的方法很奇怪。所以一个电车对象可以得到所有的电车。我认为这是实现存储库模式的一种奇怪方式。我说得对吗?
那么,这里需要什么样的抽象?是否必须有一个额外的层?如果是这样,这一层是什么样子的? (示例代码)或者我搜索的方向错误,是否有另一种解决方案可以解决存储库模式的单元测试问题?
这个架构问题我每次都遇到,一定要回答。
这个问题很难解释清楚,希望大家理解。
The result of this approach was that all my domain classes literally had no behaviour. They were just objects which holds data with no methods.
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes
that hold values with no behaviour?
不,当然不是,但这就是当今网络开发人员的教育方式。
它回到了 1970 年代的旧模块化编程风格(静态 类 + 对象中的数据)。
模块化编程导致了很多问题,所以他们发明了封装和 object-oriented 语言来解决这些问题。
制作所有数据public只要你做的是简单的网站就可以了:
- 加载数据
- 显示数据
- 保存数据
如果您需要复杂的行为,问题就开始了 - 那么您的数据实际上是软件实现的一部分,而您做到了 public!当有多个开发人员在同一个项目上工作时,这会导致严重的维护问题。
没有教 Web 开发人员 OOP。他们正在学习一种简单的编程方式 - 将他们引导到简单的网站(这是政府需要的)。
你的问题是绝对正常的,但不要指望找到绝对的答案。欢迎来到软件行业!
这是我的看法:
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes
that hold values with no behaviour?
我认为您尝试实现存储库模式,但您错过了更高的架构视图。大多数应用程序至少在 3 层中解耦:视图(演示)、业务和数据访问。
存储库模式发生在 DataAccess 中,这是您可以找到纯数据对象的地方。
但是这个数据访问层被业务层使用,你会在其中找到一个域模型,classes 具有业务行为和数据。
单元测试工作必须在业务层的域模型上进行。
这些测试不应该关心数据是如何存储的。
- Where do I call repository methods in the architecture of the application?
同样没有绝对的答案,但通常使用像业务服务这样的东西是有意义的。这些服务可以安排不同域对象之间的流,并将它们加载并保存在存储库中。
这基本上就是您在 AddFriend class 中所做的,它属于业务层。
Regarding to unit testing, we need with this approach some dependancy
injection, which says to me that it is not a independant class
业务服务通常依赖于存储库,这是单元测试的一个非常常见的情况。 Tram class 可以持有业务行为,仍然是独立的。 AddTram 业务服务需要一个存储库,依赖注入允许对其进行测试。
- Methods that insert, update and delete new entities in a database, are
they supposed to be in a class itself?
对于这一点,我可以大声说清楚:请不要那样做,是的,CRUD 操作属于 Tram Repository。这当然不是业务逻辑。这就是为什么在你的例子中你需要两个 classes 在两个不同的层:
- Tram可以是业务层的业务对象(这里没有Crud操作)
- TramRepository 是需要存储电车数据的对象(您将在其中找到 CRUD 操作)
"Because i have seen some other students making use of static methods
in a domain class which provides these functionality"
使用静态方法显然不是一个好主意,这意味着任何人都可以通过您的对象存储数据,即使它应该处理业务案例。再说一次,数据存储不是商业案例,而是技术必需品。
现在公平地说,所有这些概念都需要在上下文中进行讨论才能有意义,并且需要根据每个新项目进行调整。这就是为什么你的工作既艰巨又令人兴奋:背景为王。
我也写了一个blog article以MVVM为中心,但我认为它可以帮助理解我的答案。
在为我的 ASP.NET 项目实现存储库模式时,我遇到了一些无法解决的问题。所以我有几个关于如何正确实现存储库模式的问题。
根据我的经验,我认为 classes/models 仅在我的应用程序中没有行为,紧挨着他们的存储库不是好的 OOP。但是,这就是我实现存储库模式的方式。我只是在任何需要存储库实例的地方制作,以执行一些操作。这种方法的结果是我的所有域 classes 都没有行为。
它们只是没有方法的保存数据的对象。我的老师对我说,我用的是瘦模型,我应该努力做胖模型。 作为对该反馈的回应,我在 classes 中实现了一些业务逻辑,但我 运行 遇到了一些问题:
场景:
我的 User
class 有一个朋友列表,里面有用户对象,代表某个用户的朋友。向用户添加新朋友时,域 class 方法会检查 "friend" 是否已存在于朋友列表中。如果没有,他将被添加到列表中。这些更改需要发送到数据库以进行持久化。
我知道这必须在每个域的存储库中完成 class,但是对存储库方法的调用属于应用程序架构中的什么地方?
现在我在域 class 方法本身中调用存储库方法,以持久保存对数据库的更改:
public void AddFriend(User friend)
{
foreach(User f in Friends)
{
if(f.Username == friend.Username)
{
throw new Exception(String.Format("{0} is already a friend.", friend.Username));
}
}
Friends.Add(friend);
userRepo.AddFriend(this.Id, friend.Id);
}
这是一个好方法吗,出于某种原因我认为它不是。关于单元测试,我们需要使用这种方法进行一些依赖注入,这对我来说意味着它不是一个独立的 class(良好的可测试单元)。我读过一些人的帖子,说他们使用额外的服务层或其他东西。我认为这里需要这种抽象,但是某个服务层是什么样的?里面有什么,有哪些方法等?
我看到其他一些学生在域中使用静态方法class,它提供了添加新对象、更新对象、删除对象和获取所有对象等功能。
示例:
public class Tram
{
private static TramRepository Repo = new TramRepository(new DBTram());
public static void AddTram(int tramID, TramType type, int lineNr)
{
Tram tram = new Tram(tramID, type, TramStatus.depot, lineNr, true, null);
Repo.AddTram(tram);
}
public static List<Tram> GetAll()
{
Repo.GetAll();
}
}
我发现在域 class 中向数据库添加新实体的方法很奇怪,这就是该实体本身。同样对于 GetAll() 方法,我认为在 class 本身中有一个获取所有电车的方法很奇怪。所以一个电车对象可以得到所有的电车。我认为这是实现存储库模式的一种奇怪方式。我说得对吗?
那么,这里需要什么样的抽象?是否必须有一个额外的层?如果是这样,这一层是什么样子的? (示例代码)或者我搜索的方向错误,是否有另一种解决方案可以解决存储库模式的单元测试问题?
这个架构问题我每次都遇到,一定要回答。
这个问题很难解释清楚,希望大家理解。
The result of this approach was that all my domain classes literally had no behaviour. They were just objects which holds data with no methods.
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes that hold values with no behaviour?
不,当然不是,但这就是当今网络开发人员的教育方式。
它回到了 1970 年代的旧模块化编程风格(静态 类 + 对象中的数据)。
模块化编程导致了很多问题,所以他们发明了封装和 object-oriented 语言来解决这些问题。
制作所有数据public只要你做的是简单的网站就可以了:
- 加载数据
- 显示数据
- 保存数据
如果您需要复杂的行为,问题就开始了 - 那么您的数据实际上是软件实现的一部分,而您做到了 public!当有多个开发人员在同一个项目上工作时,这会导致严重的维护问题。
没有教 Web 开发人员 OOP。他们正在学习一种简单的编程方式 - 将他们引导到简单的网站(这是政府需要的)。
你的问题是绝对正常的,但不要指望找到绝对的答案。欢迎来到软件行业!
这是我的看法:
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes that hold values with no behaviour?
我认为您尝试实现存储库模式,但您错过了更高的架构视图。大多数应用程序至少在 3 层中解耦:视图(演示)、业务和数据访问。 存储库模式发生在 DataAccess 中,这是您可以找到纯数据对象的地方。 但是这个数据访问层被业务层使用,你会在其中找到一个域模型,classes 具有业务行为和数据。 单元测试工作必须在业务层的域模型上进行。 这些测试不应该关心数据是如何存储的。
- Where do I call repository methods in the architecture of the application?
同样没有绝对的答案,但通常使用像业务服务这样的东西是有意义的。这些服务可以安排不同域对象之间的流,并将它们加载并保存在存储库中。 这基本上就是您在 AddFriend class 中所做的,它属于业务层。
Regarding to unit testing, we need with this approach some dependancy injection, which says to me that it is not a independant class
业务服务通常依赖于存储库,这是单元测试的一个非常常见的情况。 Tram class 可以持有业务行为,仍然是独立的。 AddTram 业务服务需要一个存储库,依赖注入允许对其进行测试。
- Methods that insert, update and delete new entities in a database, are they supposed to be in a class itself?
对于这一点,我可以大声说清楚:请不要那样做,是的,CRUD 操作属于 Tram Repository。这当然不是业务逻辑。这就是为什么在你的例子中你需要两个 classes 在两个不同的层:
- Tram可以是业务层的业务对象(这里没有Crud操作)
- TramRepository 是需要存储电车数据的对象(您将在其中找到 CRUD 操作)
"Because i have seen some other students making use of static methods in a domain class which provides these functionality"
使用静态方法显然不是一个好主意,这意味着任何人都可以通过您的对象存储数据,即使它应该处理业务案例。再说一次,数据存储不是商业案例,而是技术必需品。
现在公平地说,所有这些概念都需要在上下文中进行讨论才能有意义,并且需要根据每个新项目进行调整。这就是为什么你的工作既艰巨又令人兴奋:背景为王。
我也写了一个blog article以MVVM为中心,但我认为它可以帮助理解我的答案。