metis-desk/modules/companies/src/api/domain/company.aggregate.ts

123 lines
3.8 KiB
TypeScript
Raw Normal View History

import type { Name, UniqueID } from "@repo/rdx-ddd";
import { AggregateRoot } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { CompanyError } from "./company-error";
import type { Contact, ContactProps } from "./contact.entity";
import type { Site, SiteProps } from "./site.entity";
export type CompanyProps = Readonly<{
name: Name;
active: boolean;
contacts: readonly Contact[];
sites: readonly Site[];
}>;
export class Company extends AggregateRoot<CompanyProps> {
protected constructor(props: CompanyProps, id?: UniqueID) {
super(
{
...props,
contacts: Object.freeze([...props.contacts]),
sites: Object.freeze([...props.sites]),
},
id
);
}
static create(props: Pick<CompanyProps, "name" | "active">): Result<Company, CompanyError> {
if (props.name.toPrimitive().length > 100) {
return Result.fail(new CompanyError("INVALID_REQUEST"));
}
return Result.ok(new Company({ ...props, contacts: [], sites: [] }));
}
static rehydrate(props: CompanyProps, id: UniqueID): Company {
return new Company(props, id);
}
get name() {
return this.props.name;
}
get active() {
return this.props.active;
}
get contacts() {
return this.props.contacts;
}
get sites() {
return this.props.sites;
}
update(values: Pick<CompanyProps, "name" | "active">): Result<Company, CompanyError> {
if (values.name.toPrimitive().length > 100) {
return Result.fail(new CompanyError("INVALID_REQUEST"));
}
return this.name.equals(values.name) && this.active === values.active
? Result.ok(this)
: Result.ok(new Company({ ...this.props, ...values }, this.id));
}
addContact(contact: Contact): Result<Company, CompanyError> {
if (!contact.companyId.equals(this.id)) {
return Result.fail(new CompanyError("INVALID_REQUEST"));
}
if (this.contacts.some((existing) => existing.id.equals(contact.id))) {
return Result.ok(this);
}
return Result.ok(
new Company({ ...this.props, contacts: [...this.contacts, contact] }, this.id)
);
}
updateContact(
id: UniqueID,
values: Omit<ContactProps, "companyId">
): Result<Company, CompanyError> {
const current = this.contacts.find((contact) => contact.id.equals(id));
if (!current) {
return Result.fail(new CompanyError("CONTACT_NOT_FOUND"));
}
const updated = current.update(values);
if (!updated.isSuccess) {
return updated;
}
return updated.data === current
? Result.ok(this)
: Result.ok(
new Company(
{
...this.props,
contacts: this.contacts.map((contact) =>
contact.id.equals(id) ? updated.data : contact
),
},
this.id
)
);
}
addSite(site: Site): Result<Company, CompanyError> {
if (!site.companyId.equals(this.id)) {
return Result.fail(new CompanyError("INVALID_REQUEST"));
}
if (this.sites.some((existing) => existing.id.equals(site.id))) {
return Result.ok(this);
}
return Result.ok(new Company({ ...this.props, sites: [...this.sites, site] }, this.id));
}
updateSite(id: UniqueID, values: Omit<SiteProps, "companyId">): Result<Company, CompanyError> {
const current = this.sites.find((site) => site.id.equals(id));
if (!current) {
return Result.fail(new CompanyError("SITE_NOT_FOUND"));
}
const updated = current.update(values);
if (!updated.isSuccess) {
return updated;
}
return updated.data === current
? Result.ok(this)
: Result.ok(
new Company(
{
...this.props,
sites: this.sites.map((site) => (site.id.equals(id) ? updated.data : site)),
},
this.id
)
);
}
}