Closed hikinine closed 7 months ago
Achei essa ideia sensacional
@hikinine você tem essa feature já implementada? gostei bastante, eu gostaria de testar...
@4lessandrodev tenho sim, mas em um fork com bastante modificação. Como meu intuito era aprender fui modificando algumas coisas. segue o link
https://github.com/hikinine/rich-domain
PS: não adaptei os testes. Não estão funcionando
@hikinine Eu vi as mudanças, muito bacana. Você colocou diferentes modos de rastrear as mudanças nas props. Eu não tinha pensado no proxy, mas se encaixou perfeitamente, vou seguir sua sugestão e fazer essa alteração.
Combinado, vou ir acompanhando então. Abraço
@4lessandrodev
Encontrei um possível problema com o Proxy.
Imagine a situação onde exista um Aggregate
chamado Budget
.
interface BudgetProps {
id?: Domain.UUID4,
products: Collection<Product>,
company: Company,
//...rest of properties
}
export class Budget extends Domain.Aggregate<BudgetProps> {
private constructor(props: BudgetProps) {
super(props);
}
public static create(props: BudgetProps): Either<
| DomainErrors.UnexpectedError,
| Budget
> {
return Result.ok(new Budget(props));
}
//rest of the methods...
public registerCompanyEmail(email: string) {
const emailResult = Email.create(email);
if (emailResult.isFail()) {
return emailResult
}
const value = emailResult.getValue()
//AQUI MORA O PROBLEMA
//imagine que company seja uma entidade que tem uma coleção de emails
//o método addEmail nada mais é do que um this.props.email.push(email)
//acontece que o proxy não é capaz de identificar a mudança por que não estamos necessariamente
//reescrevendo o valor de "email".
this.props.company.addEmail(value)
return Result.ok()
}
}
Se o metodo fizesse o seguinte:
this.props.email = [...this.props.email, newEmail]
mas parece uma alternativa inviável
ah não ser que a lib disponibilize uma lista que intercepta ou reescreva os métodos do array.
import { List } from 'rich-domain';
// Ao invés disso
interface Props {
emails: Array<Email>;
}
// Definir isso
interface Props {
emails: List<Email>;
}
My bad, fui ler mais sobre deep nested objects com proxy, é só reescrever o trap do get
que resolve
const handler = function (): ProxyHandler<Props> {
return {
get: function (target, prop) {
if (!isProxy(target) && isObject(target[prop])) {
return new Proxy(target[prop], handler());
}
return target[prop];
},
set: function (target, prop, value, receiver) {
const oldValue = Reflect.get(target, prop, receiver)
self.metaHistory.addSnapshot(self.props, prop, oldValue, value)
Reflect.set(target, prop, value, receiver)
return true;
}
};
};
const proxy = new Proxy<Props>(this.props, handler());
this.props = proxy
Saudações,
A ideia de criar snapshots dos estados do
Aggregate
durante as possíveis mudanças é fantástica, mas o mesmo só ocorrerá se toda mutação dos dados do agregado ocorrer por meio dos métodos implementados nagetters-and-setters
. Pra alguns cenários eu acredito ser inviável mapear todos os campos de uma propriedade com get e set, uma situação em que o domínio proíba a mutação do campoemail
por exemplo por qualquer N motivo.Sugestão:
Implementar o History do agregado utilizando o Proxy / Reflect nativo do javascript.
Algo nesse formato, a ideia ainda é a mesma mas ao invés de mapear o snapshot com um método pré estabelecido, interceptamos qualquer atribuição em
this.props
refletindo 100% o mesmo comportamento com adicional do snapshot.Você acha que seria viável?