Which one is more appropriate?
Estou quase certo de que a primeira abordagem é a que será mais saudável a longo prazo.
Enigma: se a compreensão dos negócios de OrderLine
fosse mudar durante a vida útil do modelo de domínio, com qual projeto seria mais fácil trabalhar?
No primeiro caso, você estende a mensagem ItemAdded
para incluir os novos campos necessários e atualiza o item OrderLine
para ler esses novos campos e fazer algo interessante, e é isso.
No segundo caso, você ainda precisa estender a mensagem ItemAdded, e ainda precisa modificar OrderLine
, e você também precisa modificar Order
. Isso não parece tão bom.
Parnas escreveu em ... Decompondo sistemas em módulos
We propose instead that one begins with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others.
Neste exemplo, o OrderLine é o módulo que está ocultando a decisão "como consumimos uma mensagem ItemAdded?"
I'm opening it up for possible mutation outside the context of the Order. If I provide an accessor, say, getOrderLines(), in Order, even if I make the collection/list itself unmodifiable, the objects contained will still be mutable. One way to prevent this is to return clones, but that can get a little cumbersome to implement for fairly complex nested entities.
Separação de consultas de comando e Interfaces de funções podem ajudar aqui. A idéia básica é pensar no OrderItem como tendo diferentes papéis que ele pode executar. Assim, a interface que o OrderItem implementa seria dividida em diferentes fatias, uma das quais seria um IApplyEvents
ou mesmo IHandle<ItemAdded>
, e você cria sua lógica para que as funções que permitem que o item seja modificado sejam expostas apenas no uso casos em que você os deseja.
Portanto, se você tiver um acessor como getOrderLines
, ele retornará uma coleção de RoleInterfaces que são compostos inteiramente de consultas.
interface OrderLineDetails {
ProductDetails getProductDetails();
}
interface IHandle<Event> {
void apply(Event event);
}
class OrderLine implements OrderLineDetails, IHandle<ItemAdded> {
// ...
}
Os nomes podem ficar confusos; todo mundo quer "OrderLine" para significar o que significa nesse contexto específico. O DependencyInversion pode ajudar aqui.
One way to prevent this is to return clones, but that can get a little cumbersome to implement for fairly complex nested entities.
Às vezes, suas entidades aninhadas bastante complexas são realmente valores aninhados complexos, o que pode simplificar as coisas.
State next = current.apply(change)
é bastante razoável. No resumo, de qualquer maneira. Tenha cuidado para reconhecer que complexidade vem da ideia e quais são as ferramentas que você está usando; pode ser uma sugestão de que você precisa de algo que se adapte melhor ao problema .