设计持久层

时间:2015-12-11 09:24:38

标签: c# architecture persistence

对于一个项目,我们开始研究持久性功能以及我们希望如何实现它。目前我们正在考虑保持清洁架构,可能是为了洋葱架构。因此,我们想要定义一个新的外层,持久层所在的外层。

我们正在研究使用SQLite作为数据存储的各种ORM解决方案(我们似乎正在融合到实体框架),我们正在遇到障碍:如何管理ID并处理某些集合中的添加/删除或移动一些不同集合之间的实例。

在'洋葱'的核心,我们想要保留我们的POCO对象。因此,我们不希望在业务对象中添加某种“ID”属性。只有在持久层内,我们才能拥有具有对象ID的类。由于这种分离:

  • 如何从某个集合中删除业务对象会导致从SQLite数据库中删除一行?
  • 更复杂(至少我认为是这样),POCO实例应该如何从1个集合移动到另一个集合导致SQLite数据库的外键被更改? (而不是删除行并使用相同的值重新创建它)

环顾互联网我还没有找到一个在Clean Architecture设计中展示持久层的实现。大量的高级图表和“仅向内依赖”,但没有源代码示例来演示。 到目前为止我们提出了一些可能的解决方案:

  1. 在持久层内的POCO实例与其代表性的“数据库模型对象”(具有ID等)之间进行一些查找。保存项目状态时,业务模型对象将与此数据库模型对象匹配,并相应地更新匹配的状态。然后该对象被持久化。
  2. 加载项目时,持久层会返回业务对象的装饰器对象,这些业务对象将ID添加到业务对象,该对象只能通过将对象强制转换为该装饰器类在持久层中可见。然而,这阻止我们定义密封的POCO对象,并且似乎打破了清洁架构设计理念。
  3. 由于有效地将内存中的业务对象加倍,选项1在内存中看起来很昂贵。选项2似乎是最优雅的,但正如我所写:它感觉它打破了清洁架构。

    那里有更好的替代方案吗?我们是否应该选择备选方案2并将清洁架构作为指导而非规则?有人能指出我们在代码中的一个工作示例(我确实在https://github.com/luisobo/clean-architecture找到了一个iOs示例,但由于我在语言中没有识字,因此无法用它做很多事情。)

2 个答案:

答案 0 :(得分:2)

正如其他人在评论中提到的,ID是应用程序的自然部分,并且通常在持久性以外的其他部分中是必需的。 因此,不惜一切代价避免使用ID会产生尴尬的设计。

身份设计

然而,身份设计(在哪里使用哪些ID,在ID中放入什么信息,用户定义与系统生成等)是非常重要的,需要思考。

确定需要ID的内容的良好起点,以及域驱动设计的价值对象/实体区别。

  • 价值对象是由其他价值观组成并且不会改变的东西 - 因此您不需要ID。
  • 实体具有生命周期并随时间而变化。因此,仅靠它们的价值还不足以识别它们 - 它们需要一个明确的ID。

如您所见,推理与您提出问题的技术观点截然不同。但这并不意味着您应该忽略框架(例如实体框架)所施加的约束。

如果您想深入讨论身份设计,我可以推荐"实施DDD"作者:Vaughn Vernon(第34节;独特身份"第5章 - 实体)。

注意:我意味着建议您使用DDD。我只是认为DDD有一些关于ID设计的好指南。是否在这个项目中使用DDD是一个完全不同的问题。

答案 1 :(得分:0)

首先,现实世界中的一切都有ids。你有社会安全号码。汽车有他们的注册号码。商店中的商品具有EAN代码(和生产标识)。没有ids世界上没有任何东西可以工作(有点夸张,但希望你明白我的观点)。

与应用程序相同。

如果您的业务对象没有任何自然键(如社会安全号码),您必须有办法识别它们。一旦您复制对象或将其转移到进程边界上,您的应用程序将会失败。 因为那时它是一个新对象。 就像你克隆羊多莉一样。它是同一只羊吗?不,它是Mini-Dolly。

另一部分是当你构建复杂的结构时,你违反了得墨忒耳的法则。比如:

public class ForumPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public User Creator { get; set; }
}

public class User
{
    public string Id { get; set; }
    public string FirstName { get; set; }
}

使用该代码并调用时:

post.User.FirstName = "Arnold"; 
postRepos.Update(post);
你期望发生什么?您的论坛帖子repos是否应该突然对用户所做的更改负责?

这就是为什么ORM太可怕了。他们违反了良好的建筑。

回到ids。一个好的设计是使用 用户ID 。因为那时我们没有违反得墨忒耳的法律,并且仍然有很好的分离关注。

public class ForumPost
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public int CreatorId { get; set; }
}

所以结论是:

  1. 不要放弃ID,因为它会在尝试从获取的所有副本中识别真实对象时引入复杂性。

  2. 在引用不同实体时使用ID可以帮助您保持良好的设计并承担不同的责任。