metis-desk/modules/companies/src/api/infrastructure/persistence/sequelize-company.repository.ts

106 lines
3.7 KiB
TypeScript
Raw Normal View History

import type { UniqueID } from "@repo/rdx-ddd";
import type { Result as DomainResult } from "@repo/rdx-utils";
import { Maybe, Result } from "@repo/rdx-utils";
import type { Sequelize, Transaction } from "sequelize";
import { Op } from "sequelize";
import type { ICompanyRepository } from "../../application/company.repository";
import type { Company } from "../../domain/company.aggregate";
import { CompanyError } from "../../domain/company-error";
import { CompanyModel, ContactModel, SiteModel } from "./company.model";
import type { CompanyPersistenceMapper } from "./company-persistence.mapper";
export class SequelizeCompanyRepository implements ICompanyRepository {
constructor(
private readonly database: Sequelize,
private readonly mapper: CompanyPersistenceMapper
) {}
async list(): Promise<readonly Company[]> {
const companies = await CompanyModel.findAll({
order: [
["name", "ASC"],
["id", "ASC"],
],
});
if (companies.length === 0) {
return [];
}
const ids = companies.map((company) => company.id);
const [contacts, sites] = await Promise.all([
ContactModel.findAll({
where: { companyId: { [Op.in]: ids } },
order: [
["name", "ASC"],
["id", "ASC"],
],
}),
SiteModel.findAll({
where: { companyId: { [Op.in]: ids } },
order: [
["name", "ASC"],
["id", "ASC"],
],
}),
]);
return companies.map((company) =>
this.mapper.toDomain({
company,
contacts: contacts.filter((contact) => contact.companyId === company.id),
sites: sites.filter((site) => site.companyId === company.id),
})
);
}
private async load(id: UniqueID, transaction?: Transaction): Promise<Maybe<Company>> {
const options = transaction ? { transaction, lock: transaction.LOCK.UPDATE } : {};
const company = await CompanyModel.findByPk(id.toPrimitive(), options);
if (!company) {
return Maybe.none();
}
const childOptions = {
where: { companyId: company.id },
...(transaction ? { transaction } : {}),
};
const contacts = await ContactModel.findAll({ ...childOptions, order: [["name", "ASC"]] });
const sites = await SiteModel.findAll({ ...childOptions, order: [["name", "ASC"]] });
return Maybe.some(this.mapper.toDomain({ company, contacts, sites }));
}
findById(id: UniqueID) {
return this.load(id);
}
async create(company: Company): Promise<Company> {
await CompanyModel.create(this.mapper.toPersistence(company).company);
return company;
}
async update(
id: UniqueID,
change: (company: Company) => DomainResult<Company, CompanyError>
): Promise<DomainResult<Company, CompanyError>> {
return this.database.transaction(async (transaction) => {
const current = (await this.load(id, transaction)).getOrUndefined();
if (!current) {
return Result.fail(new CompanyError("COMPANY_NOT_FOUND"));
}
const changed = change(current);
if (!changed.isSuccess || changed.data === current) {
return changed;
}
const record = this.mapper.toPersistence(changed.data);
await CompanyModel.update(record.company, { where: { id: id.toPrimitive() }, transaction });
if (record.contacts.length > 0) {
await ContactModel.bulkCreate(record.contacts, {
transaction,
updateOnDuplicate: ["name", "position", "email", "phone", "active"],
});
}
if (record.sites.length > 0) {
await SiteModel.bulkCreate(record.sites, {
transaction,
updateOnDuplicate: ["name", "address"],
});
}
return changed;
});
}
}