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 { 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> { 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 { await CompanyModel.create(this.mapper.toPersistence(company).company); return company; } async update( id: UniqueID, change: (company: Company) => DomainResult ): Promise> { 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; }); } }