@mbc-cqrs-serverless/core 1.0.16 → 1.0.17

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