复杂实体转map_一次增加一个实体的复杂度

复杂实体转map

在使用我们的一种内部工具时,我决定做一个很小的包含而不遵循我自己的建议。 我们正在构建一个迷你CRM工具,最初的要求是:

  • 维护有关我们正在处理的公司的信息;
  • 维护每个公司的联系人列表;
  • 维护每个公司的参与清单(项目,培训,咨询)。

注意:为了使本文简单,我将省略代码,属性等的详细信息。

从小开始,在为公司建立CRUD的同时,我得到了一个公司实体,看起来像这样:

class Company {
     + id: CompanyId
     + name: String
}

一切都很好。 然后,我需要编写代码以维护每个公司的联系人列表。 我得出以下结论:

class Contact {
     + id: ContactId;
     + companyId: CompanyId;
     + name: String
     + email: String;
}

class Company {
     + id: CompanyId;
     + name: String;
     + contacts: List[Contact];
}

那是问题的开始。 对于“查看公司”页面,我需要显示与公司及其所有联系人有关的数据。 对于仅处理公司数据的页面(列出所有公司的页面,用于编辑/删除公司的页面),我不需要联系信息。 每次我加载公司时都应该加载联系人吗? 我不应该加载它们吗? 在某些情况下不加载联系人的问题是,随着代码的发展,我不知道Company内部的联系人列表是否为空,因为该公司没有联系人或未加载他们。 令人困惑。 由于在此应用程序中性能不是问题,因此我决定每次需要公司时都加载联系人列表。 问题解决了。

在下一个功能中,我必须保持公司的参与度(CRUD)。 遵循与联系人相同的方法,最终得到以下实体:

class Engagement {
     + id: EngagementId;
     + companyId: CompanyId;
     + name: String
     + startDate: Date;
     + endDate: Date;
     + description: String;
}

class Company {
     + id: CompanyId;
     + name: String;
     + contacts: List[Contact];
     + engagements: List[Engagement];
}

在这一点上,事情变得非常混乱。 我的页面需要公司及其联系方式和参与度。 仅需要公司和参与的页面,仅需要公司和联系的页面。 但是问题不仅与加载内容和加载位置有关。 我有很多依赖于Company结构的代码。

该应用程序是一个Web应用程序,其前端使用AngularJS,而JSON则进入浏览器并返回到应用程序中。 为此,我有JSON转换器,可以将JSON与对象之间进行转换。 我还对API和内部层进行了大量测试,这些测试将使用构建器来组装数据。 总之,为了满足所有功能,有很多代码将依赖于Company实体的结构。 此代码“必须知道”何时加载和不加载联系人和参与。 当然,在我们决定每页需要多少信息时,这种情况一直在变化。

随着功能的稳定以及我在代码中进行了一些其他更改,一切正常。

涟漪效应

当我们认为准备开始构建其他功能(仪表板,财务信息,预测,注释,提醒,跟进操作等)时,我们意识到我们错过了一些重要的东西。

我们的某些项目来自合作伙伴(其他公司)。 这意味着一项业务可能涉及多个公司。 这可能会使“公司”与“参与”之间的关系有所不同。 也许公司与敬业度之间的关系不再是一对多的了。 这可能是很多很多

class Engagement {
     + id: EngagementId;
     + companies: List[Company];
     + name: String
     + startDate: Date;
     + endDate: Date;
     + description: String;
}

class Company {
     + id: CompanyId;
     + name: String;
     + contacts: List[Contact];
     + engagements: List[Engagement];
}

我以为这是一个容易的更改,但是我很惊讶地看到它在我的代码中产生了巨大的连锁React。 测试数据,构建器,JSON解析器和API结构的负载会受到影响,这并不是一种好感觉。 老实说,我对自己感到很失望,很生气。

遵循我自己的建议

几年前,我遇到了CQS ,后来遇到了CQRS 。 刚开始时,我并没有给予CQS太多的关注,而只有CQRS才使我真正理解了另一种设计软件的方式。 从那时起,我一直坚决主张将用于写入的数据结构与用于读取的数据结构分开(是的,我将实体视为数据结构)。 我不是在谈论可独立部署的读/写模型,不同的数据库,事件,消息等。我只是在谈论使用不同的对象来读写数据。

解决问题(第一种解决方案)

经过另一次讨论,我们认为参与始终是公司的事,但它可能是通过合作伙伴来进行的。 那又改变了事情。 因此,我决定执行以下操作:从所有实体中删除所有依赖项(包含其他实体或实体列表的属性)。 然后像这样结束:

class Company {
     + id: CompanyId
     + name: String
}

class Contact {
     + id: ContactId;
     + companyId: CompanyId;
     + name: String
     + email: String;
}

class Engagement {
     + id: EngagementId;
     + companyId: CompanyId;
     + partnerId: Optional[CompanyId];
     + name: String
     + startDate: Date;
     + endDate: Date;
     + description: String;
}

通过这种方法,实体将仅包含需要持久化的数据。

但是我仍然有需要解决的查询,其中许多查询将需要这些实体的组合。 为此,我创建了“读取”对象,该对象将完全包含每个查询所需的数据。 其中一些看起来像这样:

class CompanyWithContacts {
     + company: Company;
     + contacts: List[Contacts]
}

class CompanyWithContactsAndEngagements {
     + company: Company;
     + contacts: List[Contacts];
     + engagements: List[Engagements];
}

class EngagementWithCompanies {
     + engagement: Engagement;
     + client: Company;
     + partner: Optional[Company];
}

使用这种方法,每个查询将返回传递机制(网站上的页面)所请求的数据组合。 我的实体之间相互关系的更改不再引起更改的连锁React,因为只有特定的查询才会中断。 懒加载/渴望获取不再有问题。 毫无疑问,因为没有属性了,所以没有空属性。 可选的那些可以很容易地标记为可选(感谢Java 8)。

解决问题(第二个解决方案)

经过上述修复后,我感到很高兴,因为当实体关系发生更改时,我能够本地化更改。 但是还有更多的东西。 从积极的方面来说,他们使我可以从需要数据组合的页面中进行单个呼叫。 消极的一面,性能并不是我真正关心的问题,我也不希望这些多余的对象带有奇怪的名字。 我仍然必须编写代码来填充它们并将它们转换为JSON。

然后,我决定从我的页面打多个电话。 如果页面需要公司,联系人列表和与该公司相关的业务列表,我将在页面中打三个电话。 这个决定使所有“读取”对象消失了,并且仍然使我的代码非常简单。

结论

使您的实体彼此分离,并专注于从客户端实施简单查询。 如果性能确实被证明是一个问题,只需移至单个查询即可。

不要使用ORM。 ORM使我的更改更加糟糕,因为我必须保持实体和数据库同步。 可以自由使用所需的任何查询从数据库中获取记录集,并以所需的方式填充对象,这非常好。

查询数据更改的方式比持久化数据的方式要频繁得多,并且这些更改可以采用许多不同的方式对数据进行切片和分割。 将您的实体绑定在一起只会使满足所有查询变得更加困难,并且只会给假定运行业务逻辑和存储数据的代码带来不必要的压力。

翻译自: https://www.javacodegeeks.com/2015/08/increasing-complexity-one-entity-at-a-time.html

复杂实体转map

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值