Giao diện
@digiforce-nc/data-source-manager
| npm | @digiforce-nc/data-source-manager |
| Import | import { DataSourceManager, DataSource, SequelizeDataSource } from '@digiforce-nc/data-source-manager' |
| Runtime | Node.js |
Tổng quan
@digiforce-nc/data-source-manager cho phép hệ thống kết nối đồng thời nhiều database / data source. Mỗi data source có pipeline riêng biệt gồm:
- CollectionManager - quản lý schema (collection/field)
- ResourceManager - xử lý HTTP action routing
- ACL - kiểm soát quyền truy cập
Request được route tới đúng data source thông qua header x-data-source:
Class hierarchy
DataSourceManager API
Core methods
| Method | Mô tả |
|---|---|
add(dataSource, options?) | Thêm data source. Chạy beforeAddHooks, load data source, emit afterAddDataSource. |
get(name) | Lấy DataSource instance theo tên. |
getList() | Lấy tất cả data source đã đăng ký. |
Middleware
| Method | Mô tả |
|---|---|
use(middleware, options?) | Đăng ký middleware cấp manager - chạy cho tất cả data source. |
middleware() | Trả về Koa middleware. Middleware này đọc header x-data-source để route request tới đúng data source pipeline. |
Lifecycle
Auto-cleanup
Constructor DataSourceManager bind app.on('beforeStop', ...) để đóng tất cả data source khi app stop. Không cần cleanup thủ công.
Ví dụ sử dụng
typescript
const dsm = app.dataSourceManager;
// Thêm data source từ external database
dsm.add(new SequelizeDataSource({
name: 'crm-db',
database: {
dialect: 'mysql',
host: 'crm-server',
port: 3306,
database: 'crm',
username: 'reader',
password: 'secret',
},
}));
// Lấy data source
const crmDS = dsm.get('crm-db');
const contacts = await crmDS.collectionManager
.getCollection('contacts')
.repository
.find({ filter: { status: 'active' } });DataSource API
DataSource là abstract class - mọi data source cụ thể đều kế thừa nó.
Lifecycle methods
| Method | Mô tả |
|---|---|
init(options?) | Khởi tạo ACL, ResourceManager, CollectionManager. Đăng ký default action handlers. |
load(options?) | Load collections, sync schema nếu cần. |
createCollectionManager() | Factory method - tạo CollectionManager instance. Override trong subclass. |
createResourceManager() | Factory method - tạo ResourceManager instance. |
Properties
| Property | Kiểu | Mô tả |
|---|---|---|
name | string | Tên unique của data source |
collectionManager | CollectionManager | Quản lý collection schema |
resourceManager | ResourceManager | Quản lý resource routing |
acl | ACL | Access control cho data source này |
Init flow chi tiết
SequelizeDataSource
Concrete implementation dùng Sequelize/Database làm backend.
| Feature | Mô tả |
|---|---|
database | Instance Database - full ORM access |
SequelizeCollectionManager | CollectionManager wrapper trên Database, hỗ trợ Sequelize-specific features |
| Auto-sync | Có thể tự đồng bộ schema khi load |
collectionToResourceMiddleware
Middleware tự động tạo Resource cho mỗi Collection trong data source. Khi CollectionManager define một collection, middleware này đảm bảo có resource tương ứng trong ResourceManager.
Điều này có nghĩa: khi bạn define collection, hệ thống tự động tạo REST API tương ứng mà không cần viết route thủ công.
typescript
// Chỉ cần define collection
db.collection({
name: 'products',
fields: [
{ type: 'string', name: 'name' },
{ type: 'decimal', name: 'price' },
],
});
// Tự động có các endpoint:
// GET /api/products:list
// GET /api/products:get?filterByTk=1
// POST /api/products:create
// POST /api/products:update?filterByTk=1
// POST /api/products:destroy?filterByTk=1loadDefaultActions
Hàm loadDefaultActions() đăng ký bộ action handler CRUD chuẩn cho mỗi data source:
| Action | Handler | Mô tả |
|---|---|---|
list | actions.list | Tìm nhiều records với filter, sort, pagination |
get | actions.get | Lấy một record theo primary key |
create | actions.create | Tạo record mới |
update | actions.update | Cập nhật record |
destroy | actions.destroy | Xóa record |
add | actions.add | Thêm association (BelongsToMany) |
remove | actions.remove | Gỡ association |
set | actions.set | Set association (HasOne, BelongsTo) |
toggle | actions.toggle | Toggle association |
move | actions.move | Thay đổi sort order |
firstOrCreate | actions.firstOrCreate | Tìm hoặc tạo |
updateOrCreate | actions.updateOrCreate | Tìm cập nhật hoặc tạo |
DatabaseIntrospector
DatabaseIntrospector (trong DatabaseDataSource) cho phép đọc schema từ database thực tế - hữu ích khi kết nối tới database đã có sẵn data.
| Method | Mô tả |
|---|---|
readTables() | Đọc danh sách tables từ DB schema thực tế |
loadTables() | Tạo Collection cho mỗi table đọc được |
syncFieldsFromDatabase() | Đồng bộ field definition từ schema thực tế |
mergeWithLoadedCollections() | Gộp collections từ introspection với collections đã define |
Use case
Khi kết nối tới CRM database có sẵn, dùng introspector để tự động tạo collections từ existing tables mà không cần define từng collection thủ công.
x-data-source header routing
Khi client cần truy cập data source khác (không phải main), thêm header x-data-source:
typescript
// Client request tới external data source
const response = await apiClient.request({
url: '/api/contacts:list',
headers: {
'x-data-source': 'external-crm',
},
});Routing flow
Bảo mật
Mỗi data source có ACL riêng. Khi route tới external data source, ACL của data source đó quyết định quyền truy cập, không phải ACL của main data source.
Ví dụ tổng hợp
Đăng ký multiple data sources
typescript
import { Application } from '@digiforce-nc/server';
import { SequelizeDataSource } from '@digiforce-nc/data-source-manager';
const app = new Application({
database: {
dialect: 'postgres',
host: 'localhost',
database: 'main_app',
username: 'admin',
password: 'secret',
},
});
// Main data source được tạo tự động
// Thêm external CRM database
app.dataSourceManager.add(new SequelizeDataSource({
name: 'crm',
database: {
dialect: 'mysql',
host: 'crm-host',
database: 'crm_db',
username: 'reader',
password: 'crm-pass',
},
collections: [
{
name: 'contacts',
fields: [
{ type: 'string', name: 'firstName' },
{ type: 'string', name: 'lastName' },
{ type: 'string', name: 'email' },
{ type: 'string', name: 'phone' },
],
},
],
}));
// Thêm analytics database (read-only)
app.dataSourceManager.add(new SequelizeDataSource({
name: 'analytics',
database: {
dialect: 'postgres',
host: 'analytics-host',
database: 'analytics_db',
username: 'readonly',
password: 'analytics-pass',
},
acl: false, // Không dùng ACL cho analytics
}));Truy cập data source trong plugin
typescript
class MyCRMPlugin extends Plugin {
async load() {
// Đăng ký custom action trên external data source
const crmDS = this.app.dataSourceManager.get('crm');
crmDS.resourceManager.registerActionHandler('contacts:import', async (ctx, next) => {
// import logic
await next();
});
// Middleware cho tất cả data sources
this.app.dataSourceManager.use(async (ctx, next) => {
console.log(`Data source: ${ctx.dataSource.name}`);
await next();
});
}
}Dependencies
| Package | Vai trò |
|---|---|
@digiforce-nc/database | Database/Sequelize cho SequelizeDataSource |
@digiforce-nc/resourcer | ResourceManager per data source |
@digiforce-nc/acl | ACL per data source |
@digiforce-nc/actions | Default CRUD action handlers |
Xem thêm
- DataSourceManager - phân tích code-level chi tiết
- Server Application - cách Application tạo và quản lý DataSourceManager
- Server Architecture - kiến trúc tổng thể
- FAQ - câu hỏi thường gặp
- Mã nguồn