Use Unit Of Wrok pattern to wrap sequelize up and make sequelize easy to use.
- sequelize-adapter@2.x.x. only support Node 10 or above.
- if your node environment is old, please choose sequelize-adapter@1.x.x..
Install the components
npm install sequelize-adapter sequelize --save
- use the singleton pattern to design
- use the unit of work pattern to wrap sequelize
- use trasation feature to create, update, delete by default
- support retrying feature
- support ES2015 module
You can use TypeScirpt or JavaScript. Up to you.
create your classes what you need.
// myUnitOfWork.ts
import { Sequelize } from 'sequelize';
import { IChangeObject } from '../src/IChangeObject';
import { UnitOfWorkBase } from '../src/unitOfWorkBase';
import { UserRepository } from './userRepository';
export class MyUnitOfWork extends UnitOfWorkBase {
private static _instance: Sequelize;
static async getInstance() {
if (!MyUnitOfWork._instance) {
// setup db => test in memory
const setting = {
host: 'localhost',
dbName: 'test_db',
username: 'root',
password: '1234',
type: 'sqlite', // 'mysql'
};
const s = new Sequelize(setting.dbName, setting.username, setting.password, {
dialect: 'sqlite',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
logging: false,
});
try {
await s.authenticate();
console.log('connect db successfully.');
} catch (err) {
throw err;
}
MyUnitOfWork._instance = s;
}
return new this(MyUnitOfWork._instance);
}
private constructor(public db: Sequelize) {
super();
this.init();
}
reps = {
user: new UserRepository(this),
};
beforeSaveChange(addedEntities: IChangeObject[], updatedEntities: IChangeObject[], deletedEntities: IChangeObject[]) {
// do something...
}
afterSaveChange() {
// do something...
}
async close() {
MyUnitOfWork._instance = null;
await this.db.close();
}
private init() {
// setup retrying setting
this.retryingOption = {
count: 3,
watingMillisecond: 1000
};
// setup repositories
this.__reps = this.reps;
}
}
// userRepository.ts
import { DataTypes, ModelAttributes } from 'sequelize';
import { RepositoryBase } from '../src/repositoryBase';
import { UnitOfWorkBase } from '../src/unitOfWorkBase';
import { IUserEntity } from './IUserEntity';
export class UserRepository extends RepositoryBase<IUserEntity> {
get tableName(): string {
return 'users';
}
constructor(unitOfWork: UnitOfWorkBase) {
super(unitOfWork);
}
get schema(): ModelAttributes {
return {
id: {
type: DataTypes.UUID,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
age: {
type: DataTypes.INTEGER,
allowNull: false,
},
birthday: {
type: DataTypes.DATE,
allowNull: false,
},
};
}
}
// IUserEntity.ts
export interface IUserEntity {
id: string;
name: string;
age: number;
birthday: Date;
}
CRUD Examples
// ==== create data =====
const mydb = await MyUnitOfWork.getInstance();
mydb.reps.user.add({
id: uuid4(),
name: `Bibby`,
age: 21,
birthday: new Date(),
});
await mydb.saveChange();
// ==== update data ====
const mydb = await MyUnitOfWork.getInstance();
const uOne = await mydb.reps.user.getFirstOrDefault<IUserEntity, IUserEntity>({ where: {id: 'xxxxx'} });
uOne.name = 'Bibby_0';
uOne.age = 28;
uOne.brithday = new Date('2011-10-10');
mydb.reps.user.update(uOne);
await mydb.saveChange();
// ==== delete data ====
const mydb = await MyUnitOfWork.getInstance();
const data = await mydb.reps.user.getAll<IUserEntity, IUserEntity>({ where: {id: 'xxxxx'} });
for (const item of data) {
mydb.reps.user.delete(item);
}
await mydb.saveChange();
// ==== get data ====
const mydb = await MyUnitOfWork.getInstance();
const data = await mydb.reps.user.getAll<IUserEntity, IUserEntity>({ where: {id: 'xxxxx'} });
const data1 = await mydb.reps.user.getFirstOrDefault<IUserEntity, IUserEntity>({ where: {id: 'xxxxx'} });
// ==== get data by sql statement ====
const mydb = await MyUnitOfWork.getInstance();
const q = `
select *
from users u
where u.age = :age
`;
const data: IUserEntity = await mydb.query(q, {
replacements: {
age: 22
},
type: QueryTypes.SELECT
});
docker run -it --rm -v $(PWD):/app -w /app node:12.18-alpine sh
or
make workspace-up