注意:此内容目前正在审核中,并且随着我不断完善我的理解并收集更多见解,可能会进行更新。请在阅读时考虑这一点,并随时提出您可能有的任何建议或更正。
我们非常感谢您的反馈,这将有助于提高本次讨论的准确性和深度。

 介绍

欢迎来到面向初学者的领域驱动设计 (DDD) 系列的第一部分。在这篇文章中,我们将探讨端口和适配器的概念,并深入研究事件驱动架构 (EDA),重点关注 Pub/Sub 模型。

一些术语(考虑图书馆管理系统示例)

领域:管理图书馆的运营。

子域:目录管理、会员管理、借阅管理。

实体:图书(标题、作者、ISBN、状态)、会员(ID、姓名、会员日期)。

聚合:图书馆(书籍和成员)。

领域服务:BorrowingService(处理借阅和归还图书)。

值对象:地址(街道、城市、邮政编码)。

领域驱动设计 (DDD) 中的适配器

定义:适配器是端口的具体实现。它们将应用程序连接到数据库、Web 服务或消息传递系统等外部系统。

 关键点

  1. 端口和适配器模式:

    • 端口:定义将核心应用程序逻辑与外部系统解耦的接口。

    • 适配器:实现这些接口以连接到特定的外部系统。

  2.  关注点分离:

    • 核心逻辑:保持独立于外部系统,纯粹关注业务规则。

    • 适配器:处理与外部系统交互的细节,使得更容易替换或修改这些交互而不影响核心逻辑。

 例子

为了说明这个概念,让我们考虑一个需要从远程 API 获取用户数据并将其保存到数据库的应用程序。以下是我们如何为此场景定义和实现端口和适配器:

定义端口(接口)

 用户存储库:

interface UserRepository {

saveUser(user: User): Promise<void>;

}

 =====还有=====

 用户API客户端:

interface UserApiClient { 
    fetchUser(userId: string): Promise&lt;User&gt;; 
}

定义适配器(具体实现)

 数据库适配器:

class DatabaseUserRepository implements UserRepository {
  async saveUser(user: User): Promise&lt;void&gt; {
    // Logic to save user to the database
    console.log(`Saving user ${user.id} to the database`);
  }
}

 API适配器:

class RemoteUserApiClient implements UserApiClient {
  async fetchUser(userId: string): Promise&lt;User&gt; {
    // Logic to fetch user from the remote API
    console.log(`Fetching user ${userId} from remote API`);
    return { id: userId, name: 'John Doe' }; // Mocked user data
  }
}

使用端口的应用程序服务

 用户服务:

class UserService {
  private readonly userRepository: UserRepository;
  private readonly userApiClient: UserApiClient;

  constructor(userRepository: UserRepository, userApiClient: UserApiClient) {
    this.userRepository = userRepository;
    this.userApiClient = userApiClient;
  }

  async fetchAndSaveUser(userId: string): Promise&lt;void&gt; {
    const user = await this.userApiClient.fetchUser(userId);
    await this.userRepository.saveUser(user);
  }
}

 要点:

适配器在核心应用程序逻辑和外部系统之间提供了一座桥梁,确保了关注点的清晰分离。这使得系统在适应变化或新的集成方面更加模块化、可维护和灵活。

[

](https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a57500f-75cf-4831-b72f-2985999aaa6a_647x204.png)

事件驱动架构(Pub/Sub 模型)

定义:事件驱动架构(EDA)是一种设计模式,其中程序的流程由事件决定。事件可以定义为状态的重大变化。 Pub/Sub(发布/订阅)模型是 EDA 的常见实现,其中:

  • 发布者:生成并发送事件。

  • 订阅者:监听事件并对事件做出反应。

 关键概念

  1. 事件:更改或重大操作的通知(例如“已下订单”、“用户注册”)。

  2. 发布者:发生某些操作时发出事件的组件。

  3. 订阅者:响应特定事件、执行任务或触发其他进程的组件。

  4. 事件总线:将事件从发布者路由到订阅者的机制。

简单说明和示例

场景:用户在电子商务网站下订单。

  1. 订单服务:下订单时发布事件。

  2. 库存服务:订阅下单事件以更新库存。

  3. 通知服务:订阅下订单事件以发送确认电子邮件。

 插图:

[

](https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fd7c28e-5244-4ba4-851c-28455beb8204_459x370.png)

带有硬编码示例的事件驱动架构(Pub/Sub 模型)

 概念:

  1. 事件:更改或重大操作的通知 ( e.g., "BookBorrowed", "BookReturned", "BookAdded").

  2. 发布者:发生某些操作时发出事件的组件。

  3. 订阅者:响应特定事件、执行任务或触发其他进程的组件。

  4. 事件总线:将事件从发布者路由到订阅者的机制。

 组件和工作流程

事件定义:定义事件的结构。事件处理程序:定义事件发生时要执行的操作。事件发布者:负责发布事件的组件。主应用程序:使用事件发布者发出事件的组件。

 分步示例

1. 定义事件处理程序:(订阅者):

  • 定义事件发生时要采取的具体操作。这些在 eventHandlers.js. 中定义

  • 例如,当发布 BookBorrowedEvent 时,处理程序会更新图书的状态、发送通知并记录事件。

eventHandlers.js:


const eventHandlers = {
  'BookBorrowedEvent': [
    (bookData) =&gt; {
      // Update the book's status in the database
      console.log(`Updating status to borrowed for book ${bookData.id}`);
      // Imagine this function updates the status in the database
    },
    (bookData) =&gt; {
      // Send a notification to the user who borrowed the book
      console.log(`Sending notification to ${bookData.user} for book ${bookData.title}`);
      // Imagine this function sends a notification
    },
    (bookData) =&gt; {
      // Log the book borrowing event
      console.log(`Logging borrow event for book ${bookData.id}`);
      // Imagine this function logs the event
    }
  ],
  'BookReturnedEvent': [
    (bookData) =&gt; {
      // Update the book's status in the database
      console.log(`Updating status to available for book ${bookData.id}`);
      // Imagine this function updates the status in the database
    },
    (bookData) =&gt; {
      // Send a notification to the librarian
      console.log(`Sending return notification for book ${bookData.title}`);
      // Imagine this function sends a notification
    }
  ],
  'BookAddedEvent': [
    (bookData) =&gt; {
      // Add the new book to the catalog
      console.log(`Adding book ${bookData.title} to the catalog`);
      // Imagine this function adds the book to the catalog
    },
    (bookData) =&gt; {
      // Update the total book count
      console.log(`Updating total book count for the library`);
      // Imagine this function updates the total book count
    },
    (bookData) =&gt; {
      // Log the book addition event
      console.log(`Logging addition of book ${bookData.title}`);
      // Imagine this function logs the event
    }
  ]
};

export default eventHandlers;

  2. 定义事件:

  • 代表系统中的重大操作或更改。

  • 示例包括 BookBorrowedEventBookReturnedEventBookAddedEvent

events.js:

class BookBorrowedEvent {
  constructor(bookData) {
    this.bookData = bookData;
  }
}

class BookReturnedEvent {
  constructor(bookData) {
    this.bookData = bookData;
  }
}

class BookAddedEvent {
  constructor(bookData) {
    this.bookData = bookData;
  }
}

export { BookBorrowedEvent, BookReturnedEvent, BookAddedEvent };

3.定义事件发布者:

  • EventPublisher 类将事件路由到其相应的处理程序。

  • 发布事件时, publish 方法会查找事件类型并使用事件数据调用每个已注册的处理程序。

eventPublisher.js:

import eventHandlers from './eventHandlers.js';

class EventPublisher {
  publish(event) {
    const eventName = event.constructor.name;
    if (eventHandlers[eventName]) {
      for (const handler of eventHandlers[eventName]) {
        handler(event.bookData);
      }
    }
  }
}

export default EventPublisher;

 四、主要应用

 应用程序.js:

[

](https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7d4d387-dbc7-41c2-8003-9cf16ac55761_540x391.png)

要点说明:在事件驱动架构中,事件可以被视为端口,事件处理程序和发布者可以被视为适配器。这种关系确保核心应用程序与外部系统保持解耦,从而促进模块化和灵活性。

 发表评论