@mbc-cqrs-serverless/core 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +188 -140
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,17 +1,22 @@
1
1
  ![MBC CQRS serverless framework](https://mbc-cqrs-serverless.mbc-net.com/img/mbc-cqrs-serverless.png)
2
2
 
3
- # MBC CQRS serverless framework Core package
3
+ # @mbc-cqrs-serverless/core
4
4
 
5
- ## Description
5
+ [![npm version](https://badge.fury.io/js/@mbc-cqrs-serverless%2Fcore.svg)](https://www.npmjs.com/package/@mbc-cqrs-serverless/core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
- The Core package provides the foundational functionality for the MBC CQRS Serverless framework. It implements:
8
+ The core package of the MBC CQRS Serverless framework, providing a complete implementation of CQRS (Command Query Responsibility Segregation) and Event Sourcing patterns for AWS serverless architectures.
8
9
 
9
- - Command Query Responsibility Segregation (CQRS) patterns
10
- - Event-driven architecture support
11
- - Data persistence and retrieval
12
- - Authentication and authorization
13
- - Multi-tenant data isolation
14
- - AWS service integrations
10
+ ![Quick Start Demo](https://mbc-cqrs-serverless.mbc-net.com/demo/quick-start.gif)
11
+
12
+ ## Features
13
+
14
+ - **CQRS Pattern**: Separate command (write) and query (read) operations for better scalability
15
+ - **Event Sourcing**: Full audit trail with versioned commands and optimistic locking
16
+ - **AWS Integration**: Built-in support for DynamoDB, Step Functions, SNS, SQS, S3, and Cognito
17
+ - **Multi-tenancy**: Tenant isolation with automatic context management
18
+ - **NestJS Framework**: Leverage dependency injection, decorators, and modular architecture
19
+ - **TypeScript First**: Full type safety and excellent IDE support
15
20
 
16
21
  ## Installation
17
22
 
@@ -19,190 +24,233 @@ The Core package provides the foundational functionality for the MBC CQRS Server
19
24
  npm install @mbc-cqrs-serverless/core
20
25
  ```
21
26
 
22
- ## Usage
27
+ ## Quick Start
23
28
 
24
- ### Basic Setup
29
+ ### 1. Configure the Module
25
30
 
26
- 1. Import and configure the core module:
27
31
  ```typescript
28
- import { CoreModule } from '@mbc-cqrs-serverless/core';
29
32
  import { Module } from '@nestjs/common';
33
+ import { CommandModule, DataService, CommandService } from '@mbc-cqrs-serverless/core';
30
34
 
31
35
  @Module({
32
36
  imports: [
33
- CoreModule.forRoot({
34
- region: 'ap-northeast-1',
35
- stage: 'dev',
37
+ CommandModule.register({
38
+ tableName: 'todo',
36
39
  }),
37
40
  ],
38
41
  })
39
- export class AppModule {}
42
+ export class TodoModule {}
40
43
  ```
41
44
 
42
- ### Command Handling
45
+ ### 2. Inject Services
43
46
 
44
- 1. Create a command:
45
47
  ```typescript
46
- import { CommandInputModel } from '@mbc-cqrs-serverless/core';
47
-
48
- export class CreateUserCommand implements CommandInputModel {
49
- readonly pk: string;
50
- readonly sk: string;
51
- readonly id: string;
52
-
53
- constructor(public readonly userId: string, public readonly userData: any) {
54
- this.pk = `USER#${userId}`;
55
- this.sk = `METADATA#${userId}`;
56
- this.id = userId;
48
+ import { Injectable } from '@nestjs/common';
49
+ import {
50
+ CommandService,
51
+ DataService,
52
+ generateId,
53
+ getUserContext,
54
+ VERSION_FIRST,
55
+ IInvoke,
56
+ } from '@mbc-cqrs-serverless/core';
57
+
58
+ @Injectable()
59
+ export class TodoService {
60
+ constructor(
61
+ private readonly commandService: CommandService,
62
+ private readonly dataService: DataService,
63
+ ) {}
64
+
65
+ async create(dto: CreateTodoDto, opts: { invokeContext: IInvoke }) {
66
+ const { tenantCode } = getUserContext(opts.invokeContext);
67
+ const pk = `TODO#${tenantCode}`;
68
+ const sk = `TODO#${Date.now()}`;
69
+
70
+ const command = {
71
+ pk,
72
+ sk,
73
+ id: generateId(pk, sk),
74
+ tenantCode,
75
+ code: sk,
76
+ type: 'TODO',
77
+ version: VERSION_FIRST,
78
+ name: dto.name,
79
+ attributes: dto.attributes,
80
+ };
81
+
82
+ return await this.commandService.publishAsync(command, opts);
83
+ }
84
+
85
+ async findOne(pk: string, sk: string) {
86
+ return await this.dataService.getItem({ pk, sk });
57
87
  }
58
88
  }
59
89
  ```
60
90
 
61
- 2. Implement a command handler:
62
- ```typescript
63
- import { CommandHandler, ICommandHandler } from '@mbc-cqrs-serverless/core';
91
+ ## Key Concepts
92
+
93
+ ### CQRS Architecture
64
94
 
65
- @CommandHandler(CreateUserCommand)
66
- export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
67
- async execute(command: CreateUserCommand): Promise<void> {
68
- // Implementation
69
- }
70
- }
71
95
  ```
96
+ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
97
+ │ Client │────▶│ CommandService │────▶│ DynamoDB │
98
+ │ (Write) │ │ (publishAsync) │ │ (Command Table) │
99
+ └─────────────┘ └─────────────────┘ └────────┬────────┘
100
+
101
+ ▼ DynamoDB Streams
102
+ ┌─────────────────┐
103
+ │ Step Functions │
104
+ │ (Data Sync) │
105
+ └────────┬────────┘
106
+
107
+ ┌─────────────┐ ┌─────────────────┐ ┌────────▼────────┐
108
+ │ Client │────▶│ DataService │────▶│ DynamoDB │
109
+ │ (Read) │ │ (getItem) │ │ (Data Table) │
110
+ └─────────────┘ └─────────────────┘ └─────────────────┘
111
+ ```
112
+
113
+ ### Command vs Data Tables
114
+
115
+ | Aspect | Command Table | Data Table |
116
+ |--------|---------------|------------|
117
+ | Purpose | Event log / Audit trail | Current state |
118
+ | Versioning | All versions stored | Latest version only |
119
+ | Sort Key | Includes version (sk#v001) | No version suffix |
120
+ | Use Case | Write operations | Read operations |
121
+
122
+ ### Version Control
72
123
 
73
- ### Event Handling
124
+ Every command includes a version number for optimistic locking:
74
125
 
75
- 1. Create an event handler:
76
126
  ```typescript
77
- import { EventsHandler, IEventHandler } from '@mbc-cqrs-serverless/core';
78
- import { UserCreatedEvent } from './user-created.event';
127
+ // VERSION_FIRST = 0 for new items
128
+ const command = { pk, sk, version: VERSION_FIRST, ... };
129
+ await commandService.publishAsync(command, opts);
79
130
 
80
- @EventsHandler(UserCreatedEvent)
81
- export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
82
- async handle(event: UserCreatedEvent): Promise<void> {
83
- // Implementation
84
- }
85
- }
131
+ // Updates require the current version
132
+ const updateCommand = { pk, sk, version: currentVersion, ... };
133
+ await commandService.publishPartialUpdateAsync(updateCommand, opts);
86
134
  ```
87
135
 
88
- ### Data Access
136
+ ## API Reference
89
137
 
90
- 1. Use the DataService for persistence:
91
- ```typescript
92
- import { DataService, InjectDataService } from '@mbc-cqrs-serverless/core';
138
+ ### CommandService
93
139
 
94
- @Injectable()
95
- export class UserService {
96
- constructor(
97
- @InjectDataService() private readonly dataService: DataService
98
- ) {}
140
+ The primary service for write operations.
99
141
 
100
- async getUser(userId: string): Promise<User> {
101
- return this.dataService.findOne({
102
- pk: `USER#${userId}`,
103
- sk: `METADATA#${userId}`,
104
- });
105
- }
106
- }
107
- ```
142
+ | Method | Description |
143
+ |--------|-------------|
144
+ | `publishAsync(input, options)` | Create or update an item asynchronously via Step Functions |
145
+ | `publishSync(input, options)` | Create or update an item synchronously (bypasses Step Functions) |
146
+ | `publishPartialUpdateAsync(input, options)` | Partial update with field merging (async) |
147
+ | `publishPartialUpdateSync(input, options)` | Partial update with field merging (sync) |
148
+ | `getItem(key)` | Get a specific command version |
149
+ | `getLatestItem(key)` | Get the latest command version |
150
+ | `duplicate(key, options)` | Duplicate an existing command |
151
+ | `reSyncData()` | Re-synchronize all data to sync handlers |
152
+
153
+ ### DataService
154
+
155
+ The primary service for read operations.
156
+
157
+ | Method | Description |
158
+ |--------|-------------|
159
+ | `getItem(key)` | Retrieve a single item by pk and sk |
160
+ | `listItemsByPk(pk, options)` | List items by partition key with filtering and pagination |
108
161
 
109
- ### Authentication & Authorization
162
+ ### Helper Functions
110
163
 
111
- 1. Implement role-based access:
112
164
  ```typescript
113
- import { RolesGuard, Roles } from '@mbc-cqrs-serverless/core';
114
-
115
- @Controller('users')
116
- @UseGuards(RolesGuard)
117
- export class UserController {
118
- @Post()
119
- @Roles('admin')
120
- async createUser(@Body() userData: CreateUserDto): Promise<void> {
121
- // Implementation
122
- }
123
- }
165
+ import {
166
+ generateId, // Generate unique ID from pk and sk
167
+ getUserContext, // Extract user info from Lambda context
168
+ addSortKeyVersion, // Add version suffix to sort key
169
+ removeSortKeyVersion, // Remove version suffix from sort key
170
+ getTenantCode, // Extract tenant code from partition key
171
+ } from '@mbc-cqrs-serverless/core';
124
172
  ```
125
173
 
126
- ### Multi-tenancy
174
+ ### Constants
127
175
 
128
- 1. Configure tenant isolation:
129
176
  ```typescript
130
- import { TenantContext, UseTenant } from '@mbc-cqrs-serverless/core';
131
-
132
- @Injectable()
133
- export class UserService {
134
- @UseTenant()
135
- async getUsersForTenant(
136
- @TenantContext() tenantId: string
137
- ): Promise<User[]> {
138
- // Implementation with automatic tenant isolation
139
- }
140
- }
177
+ import {
178
+ VERSION_FIRST, // Initial version (0)
179
+ VERSION_LATEST, // Marker for latest version (-1)
180
+ VER_SEPARATOR, // Version separator in sort key ('#')
181
+ } from '@mbc-cqrs-serverless/core';
141
182
  ```
142
183
 
143
- ### AWS Integration
184
+ ## Data Sync Handlers
185
+
186
+ Extend data synchronization to custom destinations (e.g., RDS, Elasticsearch):
144
187
 
145
- 1. Use AWS services:
146
188
  ```typescript
147
- import {
148
- StepFunctionService,
149
- NotificationService
189
+ import { Injectable } from '@nestjs/common';
190
+ import {
191
+ IDataSyncHandler,
192
+ DataSyncHandler,
193
+ CommandModel,
150
194
  } from '@mbc-cqrs-serverless/core';
195
+ import { PrismaService } from '../prisma/prisma.service';
151
196
 
197
+ @DataSyncHandler({ tableName: 'todo' })
152
198
  @Injectable()
153
- export class WorkflowService {
154
- constructor(
155
- private readonly stepFunctions: StepFunctionService,
156
- private readonly notifications: NotificationService
157
- ) {}
199
+ export class TodoDataSyncHandler implements IDataSyncHandler {
200
+ type = 'rds';
158
201
 
159
- async startWorkflow(data: any): Promise<void> {
160
- await this.stepFunctions.startExecution({
161
- stateMachineArn: 'your-state-machine-arn',
162
- input: JSON.stringify(data),
202
+ constructor(private readonly prisma: PrismaService) {}
203
+
204
+ async up(cmd: CommandModel): Promise<void> {
205
+ await this.prisma.todo.upsert({
206
+ where: { pk_sk: { pk: cmd.pk, sk: cmd.sk } },
207
+ create: { /* ... */ },
208
+ update: { /* ... */ },
163
209
  });
164
210
  }
165
- }
166
- ```
167
-
168
- ## Error Handling
169
-
170
- The core package provides standardized error handling:
171
211
 
172
- ```typescript
173
- import {
174
- CommandError,
175
- ValidationError,
176
- NotFoundError
177
- } from '@mbc-cqrs-serverless/core';
178
-
179
- @CommandHandler(UpdateUserCommand)
180
- export class UpdateUserHandler implements ICommandHandler<UpdateUserCommand> {
181
- async execute(command: UpdateUserCommand): Promise<void> {
182
- if (!command.userId) {
183
- throw new ValidationError('User ID is required');
184
- }
185
-
186
- const user = await this.userService.findById(command.userId);
187
- if (!user) {
188
- throw new NotFoundError(`User ${command.userId} not found`);
189
- }
190
-
191
- // Implementation
212
+ async down(cmd: CommandModel): Promise<void> {
213
+ // Optional: handle rollback
192
214
  }
193
215
  }
194
216
  ```
195
217
 
218
+ ## Environment Variables
219
+
220
+ | Variable | Description | Default |
221
+ |----------|-------------|---------|
222
+ | `DYNAMODB_ENDPOINT` | DynamoDB endpoint URL | AWS default |
223
+ | `DYNAMODB_REGION` | DynamoDB region | AWS default |
224
+ | `SFN_ENDPOINT` | Step Functions endpoint | AWS default |
225
+ | `SFN_REGION` | Step Functions region | AWS default |
226
+ | `SNS_ENDPOINT` | SNS endpoint | AWS default |
227
+ | `SQS_ENDPOINT` | SQS endpoint | AWS default |
228
+ | `COGNITO_ENDPOINT` | Cognito endpoint | AWS default |
229
+ | `COGNITO_REGION` | Cognito region | AWS default |
230
+
231
+ ## Related Packages
232
+
233
+ | Package | Description |
234
+ |---------|-------------|
235
+ | [@mbc-cqrs-serverless/cli](https://www.npmjs.com/package/@mbc-cqrs-serverless/cli) | CLI for project scaffolding |
236
+ | [@mbc-cqrs-serverless/sequence](https://www.npmjs.com/package/@mbc-cqrs-serverless/sequence) | Sequence number generation |
237
+ | [@mbc-cqrs-serverless/task](https://www.npmjs.com/package/@mbc-cqrs-serverless/task) | Async task processing |
238
+ | [@mbc-cqrs-serverless/master](https://www.npmjs.com/package/@mbc-cqrs-serverless/master) | Master data management |
239
+ | [@mbc-cqrs-serverless/tenant](https://www.npmjs.com/package/@mbc-cqrs-serverless/tenant) | Multi-tenancy support |
240
+ | [@mbc-cqrs-serverless/import](https://www.npmjs.com/package/@mbc-cqrs-serverless/import) | Data import utilities |
241
+ | [@mbc-cqrs-serverless/ui-setting](https://www.npmjs.com/package/@mbc-cqrs-serverless/ui-setting) | UI configuration |
242
+
196
243
  ## Documentation
197
244
 
198
- Visit https://mbc-cqrs-serverless.mbc-net.com/ to view the full documentation, including:
199
- - Architecture overview
200
- - Core concepts and patterns
201
- - AWS integration details
202
- - Security features
203
- - API reference
245
+ Full documentation is available at [https://mbc-cqrs-serverless.mbc-net.com/](https://mbc-cqrs-serverless.mbc-net.com/)
246
+
247
+ - [Getting Started](https://mbc-cqrs-serverless.mbc-net.com/docs/introduction)
248
+ - [Build a Todo App Tutorial](https://mbc-cqrs-serverless.mbc-net.com/docs/build-todo-app)
249
+ - [Architecture Guide](https://mbc-cqrs-serverless.mbc-net.com/docs/architecture-overview)
250
+ - [API Reference](https://mbc-cqrs-serverless.mbc-net.com/docs/command-service)
204
251
 
205
252
  ## License
206
253
 
207
- Copyright &copy; 2024, Murakami Business Consulting, Inc. https://www.mbc-net.com/
208
- This project and sub projects are under the MIT License.
254
+ Copyright © 2024-2025, Murakami Business Consulting, Inc. [https://www.mbc-net.com/](https://www.mbc-net.com/)
255
+
256
+ This project is under the [MIT License](../../LICENSE.txt).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mbc-cqrs-serverless/core",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "CQRS and event base core",
5
5
  "keywords": [
6
6
  "mbc",
@@ -88,5 +88,5 @@
88
88
  "serverless-step-functions-local": "^0.5.1",
89
89
  "supertest": "^7.0.0"
90
90
  },
91
- "gitHead": "4ab967474160873735b8cb816272a7012b8940d5"
91
+ "gitHead": "f94a747c55ac4b09a643a5201dc89e4edda19c76"
92
92
  }