[fabric/domain] Refactor aggregate model implementation and related types

This commit is contained in:
Pablo Baleztena 2024-10-17 14:36:49 -03:00
parent 2ed9291c4d
commit 3c3ce276c0
8 changed files with 71 additions and 45 deletions

View File

@ -0,0 +1,37 @@
import type { Keyof } from "../../core/index.ts";
import type { CustomModelFields } from "./custom-model-fields.ts";
import { DefaultEntityFields, type EntityModel } from "./entity-model.ts";
import { Field } from "./fields/index.ts";
export const DefaultAggregateFields = {
...DefaultEntityFields,
streamId: Field.uuid({ isIndexed: true }),
streamVersion: Field.integer({
isUnsigned: true,
hasArbitraryPrecision: true,
}),
deletedAt: Field.timestamp({ isOptional: true }),
};
export interface AggregateModel<
TName extends string = string,
TFields extends CustomModelFields = CustomModelFields,
> extends EntityModel<TName, TFields> {
fields: typeof DefaultAggregateFields & TFields;
}
export function defineAggregateModel<
TName extends string,
TFields extends CustomModelFields,
>(name: TName, fields: TFields): AggregateModel<TName, TFields> {
return {
name,
fields: { ...DefaultAggregateFields, ...fields },
} as const;
}
export type ModelAddressableFields<TModel extends AggregateModel> = {
[K in Keyof<TModel["fields"]>]: TModel["fields"][K] extends { isUnique: true }
? K
: never;
}[Keyof<TModel["fields"]>];

View File

@ -0,0 +1,3 @@
import type { FieldDefinition } from "./fields/index.ts";
export type CustomModelFields = Record<string, FieldDefinition>;

View File

@ -0,0 +1,14 @@
import type { CustomModelFields } from "./custom-model-fields.ts";
import { Field } from "./fields/index.ts";
import type { Model } from "./model.ts";
export const DefaultEntityFields = {
id: Field.uuid({ isPrimaryKey: true }),
};
export interface EntityModel<
TName extends string = string,
TFields extends CustomModelFields = CustomModelFields,
> extends Model<TName, TFields> {
fields: typeof DefaultEntityFields & TFields;
}

View File

@ -1,6 +1,6 @@
import { isError } from "@fabric/core";
import { describe, expect, test } from "@fabric/testing";
import { defineModel } from "../model.ts";
import { defineAggregateModel } from "../aggregate-model.ts";
import { Field } from "./index.ts";
import {
InvalidReferenceFieldError,
@ -9,7 +9,7 @@ import {
describe("Validate Reference Field", () => {
const schema = {
User: defineModel("User", {
User: defineAggregateModel("User", {
name: Field.string(),
password: Field.string(),
otherUnique: Field.integer({ isUnique: true }),

View File

@ -1,3 +1,5 @@
export * from "./aggregate-model.ts";
export * from "./custom-model-fields.ts";
export * from "./fields/index.ts";
export * from "./model-schema.ts";
export * from "./model.ts";

View File

@ -1,12 +1,13 @@
import type { PosixDate } from "@fabric/core";
import { describe, expectTypeOf, test } from "@fabric/testing";
import type { PosixDate } from "../../core/index.ts";
import type { UUID } from "../types/uuid.ts";
import { defineAggregateModel } from "./aggregate-model.ts";
import { Field } from "./fields/index.ts";
import { defineModel, type ModelToType } from "./model.ts";
import type { ModelToType } from "./model.ts";
describe("CreateModel", () => {
test("should create a model and it's interface type", () => {
const User = defineModel("User", {
const User = defineAggregateModel("User", {
name: Field.string(),
password: Field.string(),
phone: Field.string({ isOptional: true }),

View File

@ -1,10 +1,8 @@
import type { Keyof } from "@fabric/core";
import type { CustomModelFields } from "./custom-model-fields.ts";
import type { FieldToType } from "./fields/field-to-type.ts";
import { Field, type FieldDefinition } from "./fields/index.ts";
export type CustomModelFields = Record<string, FieldDefinition>;
export interface Collection<
export interface Model<
TName extends string = string,
TFields extends CustomModelFields = CustomModelFields,
> {
@ -12,53 +10,20 @@ export interface Collection<
fields: TFields;
}
export const DefaultModelFields = {
id: Field.uuid({ isPrimaryKey: true }),
streamId: Field.uuid({ isIndexed: true }),
streamVersion: Field.integer({
isUnsigned: true,
hasArbitraryPrecision: true,
}),
deletedAt: Field.timestamp({ isOptional: true }),
};
export interface Model<
TName extends string = string,
TFields extends CustomModelFields = CustomModelFields,
> extends Collection<TName, TFields> {
fields: typeof DefaultModelFields & TFields;
}
export function defineModel<
TName extends string,
TFields extends CustomModelFields,
>(name: TName, fields: TFields): Model<TName, TFields> {
return {
name,
fields: { ...DefaultModelFields, ...fields },
} as const;
}
export function defineCollection<
TName extends string,
TFields extends CustomModelFields,
>(name: TName, fields: TFields): Collection<TName, TFields> {
return {
name,
fields,
} as const;
}
export type ModelToType<TModel extends Collection> = {
export type ModelToType<TModel extends Model> = {
[K in Keyof<TModel["fields"]>]: FieldToType<TModel["fields"][K]>;
};
export type ModelFieldNames<TModel extends CustomModelFields> = Keyof<
TModel["fields"]
>;
export type ModelAddressableFields<TModel extends Model> = {
[K in Keyof<TModel["fields"]>]: TModel["fields"][K] extends { isUnique: true }
? K
: never;
}[Keyof<TModel["fields"]>];

View File

@ -1,9 +1,13 @@
import type { VariantTag } from "@fabric/core";
import type { Event } from "../events/event.ts";
import type { StoredEvent } from "../events/stored-event.ts";
import type { Model, ModelToType } from "../models/model.ts";
import type { AggregateModel } from "../models/aggregate-model.ts";
import type { ModelToType } from "../models/model.ts";
export interface Projection<TModel extends Model, TEvents extends Event> {
export interface Projection<
TModel extends AggregateModel,
TEvents extends Event,
> {
model: TModel;
events: TEvents[VariantTag][];
projection: (