前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >无处安放的代码-重读《企业应用架构模式》

无处安放的代码-重读《企业应用架构模式》

作者头像
云云众生s
发布2024-09-12 11:18:01
1220
发布2024-09-12 11:18:01
举报
文章被收录于专栏:云云众生s

作者在多年后重读《企业应用架构模式》一书,并结合自己维护的一个企业级应用的经历,对书中的模式有了更多的认识。

因缘巧合,在一个早就应该毕业的年龄,我回到了我IT生涯的起点,又来到了一个做企业级应用开发的公司。这么多年来,与这个公司忽近忽远,如今进来了,颇有些感慨。我想,我的经历应该可以带来一些不太一样的认识,后面我会分享一些我对新的框架、架构和工具的看法。

我一直以重度 Kindle 爱好者和读书人自居,不过我并不是一直保持非常高的阅读量,而是有几个明显的高峰。第一个高峰就是大学毕业后的两三年,那两年我刷成了 China Pub 的某级别的会员,应该是消费 5000 元以上。想想那时的收入和现在抠抠搜搜的我,有点不理解那时的自己。

当时应该是将许多经典书籍都买到了,例如《设计模式》、《重构》、《测试驱动开发》、《Java 安全》、《编写有效用例》等等,其中就包括了这本《企业应用架构模式》。有几本书对我影响深远,例如《重构》和《测试驱动开发》,让我养成了写单元测试的习惯,不管使用何种编程语言。还有一些就像是学会了屠龙之技,例如《Java 安全》,一直只用些皮毛,直到七八年后在 IBM 从事 WebSphere 产品支持工作时,我才终于发现当年所学有了用武之地。真的,很少有软件会像 WebSphere 这样用到了 Java 各个层面的安全机制。

回到本文的重点,当年我是匆匆读过《企业应用架构模式》,最深的印象是,突然意识到了我那个宝洁(还是雅芳)归来的领导那些怪异的程序好像都能在这本书中找到原型。可惜,之后还没来得及太多的实践,我便离开了企业级开发市场。

此后的很多年,虽然我还在广义的企业级应用市场,但是我更多的是从更“底层(中间件、JDK和OS)”去提供支持,偶尔会帮助客户“不专业的”现场程序员,但是很少会去关心“企业应用架构模式”了。

直到最近,我又有机会审视一个"积累"多年的企业级应用,一种无力感突然涌向心头,我很想有人告诉我,我看到的是什么?此刻,突然想起之前有一位推友提到重读《企业应用架构模式》,是不是我也可以?

效果出人意料地好,果然我早就知道了答案,只是在潜意识里等待被唤醒。原来,这个企业级应用竟然是一份《企业应用架构模式》的百科全书,书中的每一种模式,以及各种模式的组合都能在其代码中找到鲜活的实例。恍惚间我似乎有了一种读《旧约》的感觉,自摩西以来,面对犹太人的苦难,一代代的士师不断的反思,用各种方式在《旧约》中寻求信仰的解释,不仅没有让犹太人沉沦,反而塑造了不同一般的犹太人。同样,一代代的程序员,也努力用自己的代码在这个应用中寻求答案,终于也成了这个应用的一部分。

虽然继续拓展这个隐喻很有意思,不过这里就不继续解读了。这里还是奉劝各位在严肃的工作中应该避免轻易的使用隐喻。你觉得精妙的隐喻,别人很有可能无法共情。更重要的是,几乎所有的隐喻都会被错误的拓展。

还是回到主题,我们看看这个应用中用到了模式。很明显,我们的代码中有着大量的“交易脚本(Transaction Scripts)”:

这是一个显然的模式。在我们的这个企业级应用中,一个接口对应一个 Flow 类的 run 方法,领域逻辑就写在这个方法里。这个企业级应用有大量这样的方法,很多都非常的长。

如果要复用代码怎么办,最简单的方法就是写方法,但是 Flow 类再去调用其他 Flow 类的方法似乎不是很合适。那还有个办法就是使用继承,为相似的接口建一个父 Flow 类,编写一些不同接口都需要的方法,然后在各个接口的 Flow 类中调用。这样复用会好一些,但是显然也有人觉得使用继承可能并不直观,不如直接加个 if else 来的轻松。因此,某个子类的方法越来越膨胀,变成了另一个交易脚本。

这些“脚本”中还存在许多这样的代码:

代码语言:javascript
复制
...
//自动结清账户校验
if ((BusiUtil.isEqualY(subMobel.getAutoSettleFlag()))&&(Busiutil.isEqualN(autosettle0k))) {
  if (BusiUtil.isNotNull(mainModel.getBaseAcctNo())){
  ...

是不是看的挺累,满屏幕这样的代码确实头大。有注释让这段代码略微好读了一点,但是其他问题依然存在。

如果另外一段逻辑也需要执行这个判断,应该复制这段代码吗?如果以后判断逻辑有变化,我还需要找到所有的判断去修改吗?复制代码显然是不合适的。

既然这是判断 subModel 的一个状态,是否可以给 subMobel 增加一个判断方法?似乎应该可以。不过我们这个企业级应用中,许多类似 subMobel 的对象实际是 Row Data Gateway,也就是说对应的是数据库的一条记录,而这个对象对应的类是框架自动生成的,是不能修改的。

再加一个 subMobelUtil 类来附加这样的方法吗?这样会让代码更丑,而且其他开发者很有可能不知道有这样的类存在?等等,这好像就是《企》中的一个解决方案?

其实并不是,《企》所说的第二种“领域逻辑模式” 是领域模型(Domain Model):

图上的 Contract, Product, RecognitionStrategy 都是领域模型,即包含了数据,也包含了业务逻辑。《企》给的代码示例中,这些类完全不涉及数据库操作,不仅仅是因为数据库操作很繁琐,也是明确告诉我们,领域模型不应该与数据库操作紧密耦合,这样做有一个很大的好处,就是我们可以单独测试领域模型,这在企业级应用中无疑非常的重要。

在我们的企业级应用中,我们在业务逻辑上直接操纵 Row Data Gateway 对象,这样很直接,但是这些对象不是领域模型。面对复杂的业务时,直接操纵 Row Data Gateway 对象会让问题复杂,因此,还是应该新建一些领域模型,而通过其他方式来访问数据库。

在《企》中,当使用领域模型时,对数据库的访问推荐使用 Data Mapper :

为了更清楚的理解,我咨询 Claude 给了我一份代码:

代码语言:javascript
复制
// Domain Model
public class User {
    private int id;
    private String username;
    private String email;

    // 构造函数、getter和setter方法省略

    public void validateEmail() {
        if (!email.contains("@")) {
            throw new IllegalArgumentException("Invalid email format");
        }
    }
}

// Data Mapper
public class UserMapper {
    public User findById(int id) {
        // 模拟从数据库获取用户
        return new User(id, "user" + id, "user" + id + "@example.com");
    }

    public void save(User user) {
        // 模拟保存用户到数据库
        System.out.println("Saving user: " + user.getUsername());
    }
}

// Service
public class UserService {
    private UserMapper userMapper;

    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public User registerUser(String username, String email) {
        User newUser = new User(0, username, email);
        newUser.validateEmail();  // 调用Domain Model的业务逻辑

        userMapper.save(newUser);  // 使用Data Mapper持久化数据
        return newUser;
    }

    public User getUserById(int id) {
        return userMapper.findById(id);
    }
}

User 包含了数据和一个领域逻辑方法 validateEmail;而 UserMapper 负责数据库;UserService 负责调用 User 和 UserMapper。如果需要增加领域逻辑,直接在 User 上增加就行了。

这个例子很简单,但是正如《企》所说,简单的例子似乎无法展现领域模型模式的威力,但是我们讲东西通常希望简单明了。

回过头来看我们的企业应用。编写代码时经常会有那种无处安放的感觉,当我们不知道怎么安放时,只好就近处理,或者自己设计一种方法,但是别人又不知道你的安排,后果就是越来越乱。

《企》给我们的答案似乎相当简单,引入 Domain Model 和 Data Mapper 模式,其中 Domain Model 中编写领域逻辑,Data Mapper 负责数据库操作,而原来的 Flow 负责协调。这样我们那些复杂的判断和逻辑就有了归处,当然还需要让团队都知道这样的设计。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-112,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档