@nivinjoseph/n-domain
Version:
Domain Driven Design and Event Sourcing based framework for business layer implementation
120 lines (97 loc) • 4.39 kB
text/typescript
import { given } from "@nivinjoseph/n-defensive";
import { ApplicationException } from "@nivinjoseph/n-exception";
import { Deserializer } from "@nivinjoseph/n-util";
import { AggregateState } from "./aggregate-state.js";
import { DomainObject } from "./domain-object.js";
export class AggregateStateHelper
{
public static serializeStateIntoSnapshot(state: object, ...cloneKeys: ReadonlyArray<string>): object
{
const snapshot: Record<string, any> = Object.assign({}, state);
Object.keys(snapshot).forEach(key =>
{
const val = snapshot[key];
if (val && typeof val === "object")
{
if (cloneKeys.contains(key))
{
snapshot[key] = JSON.parse(JSON.stringify(val));
return;
}
if (Array.isArray(val))
snapshot[key] = (<Array<Object>>val).map(t =>
{
if (typeof t === "object")
return this._serializeForSnapshot(t);
else
return t;
});
else
snapshot[key] = this._serializeForSnapshot(val);
}
});
return snapshot;
}
public static deserializeSnapshotIntoState(snapshot: object): object
{
given(snapshot, "snapshot").ensureHasValue().ensureIsObject();
const deserialized: Record<string, any> = {};
Object.keys(snapshot).forEach(key =>
{
const value = (snapshot as any)[key];
if (value == null || typeof value !== "object")
{
deserialized[key] = value;
return;
}
if (Array.isArray(value))
{
deserialized[key] = value.map(v =>
{
if (v == null || typeof v !== "object" || !Deserializer.hasType(v.$typename))
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return v;
return Deserializer.deserialize(v);
});
}
else
{
deserialized[key] = Deserializer.hasType(value.$typename)
? Deserializer.deserialize(value) : value;
}
});
return deserialized;
}
public static rebaseState<T extends AggregateState>(state: T, defaultState: object, rebaseState: object, rebaseVersion: number): void
{
given(state, "state").ensureHasValue().ensureIsObject();
given(defaultState, "defaultState").ensureHasValue().ensureIsObject();
given(rebaseState, "rebaseState").ensureHasValue().ensureIsObject();
given(rebaseVersion, "rebaseVersion").ensureHasValue().ensureIsNumber().ensure(t => t > 0);
// current factory generated default state
// layer rebaseState state on top of it
// layer the above result on top of current state
defaultState = AggregateStateHelper.deserializeSnapshotIntoState(defaultState);
rebaseState = AggregateStateHelper.deserializeSnapshotIntoState(rebaseState);
// console.dir(state);
// console.dir(defaultState);
// console.dir(rebaseState);
Object.assign(state, defaultState, rebaseState);
state.isRebased = true;
state.rebasedFromVersion = rebaseVersion;
// console.dir(state);
}
private static _serializeForSnapshot(value: Object): object
{
if (value instanceof DomainObject)
return value.serialize() as object;
if (Object.keys(value).some(t => t.startsWith("_")))
throw new ApplicationException(
`attempting to serialize an object [${value.getTypeName()}] with private fields but does not extend DomainObject for the purposes of snapshot`);
return JSON.parse(JSON.stringify(value)) as object;
// given(value, "value").ensureHasValue().ensureIsObject()
// .ensure(t => !!(<any>t).serialize, `serialize method is missing on type ${value.getTypeName()}`)
// .ensure(t => typeof ((<any>t).serialize) === "function", `property serialize on type ${value.getTypeName()} is not a function`);
// return (<any>value).serialize();
}
}