(学习极客时间-如何落地业务建模 的笔记, 展示代码皆使用文中例子, 仅做学习记录)
我们知道, 聚合根实体的实例化是需要借助数据库, 但这种逻辑放在实体内, 会混杂领域模型的含义. 于是 Eric 的一书中, 采用 Repository 仓储模式单独做数据相关工作.
具体方式是定义聚合根实体的 Repository 接口, 以及 DB 实现, 将保存与查询等操作置于其中.
借用文中的例子:
class User { private List<Subscription> subscriptions; // 获取用户订阅的所有专栏 public List<Subscription> getSubscriptions() { ... } // 计算所订阅的专栏的总价 public double getTotalSubscriptionFee() { ... } } class UserRepository { ... public User findById(long id) { ... }
UserRepository 将 User 的实例化查询数据库隔离, 帮助构建包含有 Subscriptions 订阅专栏集合的 User 用户实例.
这里存在一个问题, 如果订阅专栏集合过大, 那么内存容量就是个问题. 现实场景中一般是用分页限定查询大小的. 那么就会理所当然的在 User 实例中加上一个方法.
public List<Subscription> getSubscriptions(int from, int size);
这样做是否真的能解决内存消耗过大的问题? 毕竟 UserRepository#findById 时就已经将 subscriptions 全部读取了出来 (即便是用懒加载, 其重要逻辑也需要在 Subscription 实体中编写, 超出领域行为范畴).
另一个问题是, User 中的这种行为也是超出它本身的含义, 分页这种数据库行为也需要隔离.
由此引出新的模式, 关联对象.
根据用户与订阅的关系, 我们建立一个关联对象 MySubscriptions, 含义为我的订阅.
public interface MySubscriptions extends Iterable<Subscription> { List<Subscription> subList(int from, int to); //分页 double getTotalSubscriptionFee(); //获取总共花费 int count(); //获取总订阅数 Iterable<Subscription> sort(...); .... }
那么 User 实体中, 就不直接关联 Subscription 实体了
public UserRepositoryDB implements UserRepository { ... public User findBy(long id) { User user = .....; return setMySubscription(user); } public List<User> findBy(....) { List<User> user = .....; return user.map(user -> setMySubscription(user)); } private User setMySubscription(User user) { user.setMySubscriptions(new MySubscriptionDB(db, user)); return user; } }
public class User { private MySubscriptions mySubscriptions; public MySubscriptions getMySubscriptions() { return mySubscriptions } }
当我们借助 UserRepository 实例化 User 的时候, 也不必将相关订阅全部读到内存. 要读到订阅只能通过 MySubscription 接口的分页方法, 有限度的即时读取. 而读取逻辑则归到关联对象上.
package model.impl.db; public class MySubscriptionsDB implements MySubscriptions { ... private User user; public List<Subscription> subList(int from, int to) { return db.executeQuery(...); } ...
为了保有领域模型的聚合关系, 我们会在聚合根实体中设置关联的实体, 但是当它为一对多关系时, 一次构建实体带来的性能问题就很凸显.
如何做到即保有模型, 又有好的性能, 关联对象是解决它的一种设计模式. 它就像是中间层一样, 解耦这个关联问题.