JavaScript中的领域驱动设计


让DDD为你的JavaScript混乱带来秩序。
我不会把自己当作JavaScript开发人员,我总是开玩笑说这是一种我从未打算学习的语言。它现在如此普遍,它刚刚发生。我经历了享受它和鄙视它的阶段。但是通过爱的高峰和低谷并不是很讨厌。一个问题仍然存在:如果我要成为一名优秀的JS开发人员并编写函数性JavaScript,那么我如何以使用一种合适的领域模型的方式编写代码?

在传统的OO语言中,例如Java,C#,甚至Go实际上,编写围绕领域设计构建的代码很容易。你有Class类,这些类很大并​​且能做很多事情。当然,你通常在JavaScript中会像躲避瘟疫一样避免这种情况。

但是,我的代码似乎总是看起来像这样:

const { getUser, removeUser } = require('services/user');
const { sendEmail } = require('helpers/email');
const { pushNotification } = require('helpers/notifications');
const { removeFilesByUserId } = require('services/files');
const removeUserHandler = await (userId) => {
  const message = 'Your account has been deleted';
  try {
    const user = await getUser(userId);
    await removeUser(userId);
    await sendEmail(userId, message);
    await pushNotification(userId, message);
  } catch (e) {
    console.error(e);
    sendLogs('removeUserHandler', e);
  };
  return true;
};

看起来没问题吧?当然!这里设计没有大问题。但是,如果你有一个完全由这样的文件构成的大型代码库,换句话说,目录中充满了模糊分组的“服务”,单独导出和导入单个函数,通常含糊不清,并且在阅读时通常不属于某个域代码,它可以很快地感觉到你正在处理一个大泥球,而不是一个架构良好的软件应用程序。

我不想回到类和传统的封装。在学习'functional way 之后,这感觉就像退后一步。但是,越来越多,我发现JavaScript项目难以阅读,支离破碎。我到处都看到了这个!看起来JS项目几乎没有设计或架构似乎很常见。我已经准备好把JS扔进垃圾桶并转投到Golang。

直到我的一位工程师将一个新功能放入我们最嘈杂的代码库之一,这引起了我的注意。

ScheduledJobs.run(jobId);
const job = await ScheduledJobs.get(jobId);

这要做成一个类吗?当然不是:

const run = (jobId) => {};
const stop = (jobId) => {};
const pause = (jobId) => {};
const get = (jobId) => {};
module.exports = {
 run,
 stop,
 pause,
 get,
};

整个代码非琐碎,其实没有javascript更重要概念,模块,模块其实类似“类"或聚合,是一种相似功能的聚合而已。重构代码如下,加入模块概念:


const UserModel = require('models/user');
const EmailService = require('services/email');
const NotificationService = require('services/notification');
const FileModel = require('models/file');
const Logger = require('services/logger');
const removeUserHandler = await (userId) => {
  const message = 'Your account has been deleted';
  try {
    const user = await UserModel.getUser(userId);
    await UserModel.removeUser(userId);
    await EmailService.send(userId, message);
    await NotificationService.push(userId, message);
    return true;
  } catch (e) {
    console.error(e);
    Logger.send('removeUserHandler', e);
  };
  return true;
};

将函数进行分组形成对象,以对象为中心编码,这种模式比处理大量一个个未分组的函数调用能更好地传达了目的。我发现它让代码更容易理解,这段代码适合指明大局的指标。