2 added 363 characters in body
source | link

I have an invoice aggregate that I create with a lot of data:

$invoice = Invoice::create(
    InvoiceId::generate(),
    $lines, // Collection of lines
    InvoiceNumber::create('1'),
    $address, // Address value object
    Currency::EUR(),
    null,
    $discounts // Collection of discounts for lines and the invoice itself
);

So this works perfectly fine when I create a new invoice or edit an existing invoice. But what if I just want to cancel an invoice?

The CancelInvoiceCommand contains just the identifier and the handler then needs to instantiate the aggregate. But I don't have nor do I need all the data above. So can I simply do something like this?

$invoice = Invoice::createForStateChange($invoiceId);
$invoice->cancel();

The problem with that is, it will use the same aggregate object that implements Invoice::create() as well. The problem I try to understand is, that when I do that, the aggregate will be in an invalid state because it has a lot other methods as well to operate on the data that is available when create() was used but not when createForStateChange() was used.

Do I have to always create an aggregate root with all available data or can I have also another aggregate that addresses the same domain but a different process / part of it? So I would have a more simplified aggregate that just relies on having the id present?


After reading this questions accepted answer Confusion about the meaning of the word aggregate in domain driven design

The key part of the answer (at least for me) is:

Partial loading of an aggregate is broken when trying to apply a change -- how could a well designed aggregate possible validate all of its consistency rules with a subset of the data? It's certainly the case that, if you have a requirement where this makes sense, your modeling is broken somewhere.

I think I begin to understand: I always want to reconstruct the whole entity to ensure it's state is correct. So I would have to always re-create the whole entity?

I have an invoice aggregate that I create with a lot of data:

$invoice = Invoice::create(
    InvoiceId::generate(),
    $lines, // Collection of lines
    InvoiceNumber::create('1'),
    $address, // Address value object
    Currency::EUR(),
    null,
    $discounts // Collection of discounts for lines and the invoice itself
);

So this works perfectly fine when I create a new invoice or edit an existing invoice. But what if I just want to cancel an invoice?

The CancelInvoiceCommand contains just the identifier and the handler then needs to instantiate the aggregate. But I don't have nor do I need all the data above. So can I simply do something like this?

$invoice = Invoice::createForStateChange($invoiceId);
$invoice->cancel();

The problem with that is, it will use the same aggregate object that implements Invoice::create() as well. The problem I try to understand is, that when I do that, the aggregate will be in an invalid state because it has a lot other methods as well to operate on the data that is available when create() was used but not when createForStateChange() was used.

Do I have to always create an aggregate root with all available data or can I have also another aggregate that addresses the same domain but a different process / part of it? So I would have a more simplified aggregate that just relies on having the id present?

I have an invoice aggregate that I create with a lot of data:

$invoice = Invoice::create(
    InvoiceId::generate(),
    $lines, // Collection of lines
    InvoiceNumber::create('1'),
    $address, // Address value object
    Currency::EUR(),
    null,
    $discounts // Collection of discounts for lines and the invoice itself
);

So this works perfectly fine when I create a new invoice or edit an existing invoice. But what if I just want to cancel an invoice?

The CancelInvoiceCommand contains just the identifier and the handler then needs to instantiate the aggregate. But I don't have nor do I need all the data above. So can I simply do something like this?

$invoice = Invoice::createForStateChange($invoiceId);
$invoice->cancel();

The problem with that is, it will use the same aggregate object that implements Invoice::create() as well. The problem I try to understand is, that when I do that, the aggregate will be in an invalid state because it has a lot other methods as well to operate on the data that is available when create() was used but not when createForStateChange() was used.

Do I have to always create an aggregate root with all available data or can I have also another aggregate that addresses the same domain but a different process / part of it? So I would have a more simplified aggregate that just relies on having the id present?


After reading this questions accepted answer Confusion about the meaning of the word aggregate in domain driven design

The key part of the answer (at least for me) is:

Partial loading of an aggregate is broken when trying to apply a change -- how could a well designed aggregate possible validate all of its consistency rules with a subset of the data? It's certainly the case that, if you have a requirement where this makes sense, your modeling is broken somewhere.

I think I begin to understand: I always want to reconstruct the whole entity to ensure it's state is correct. So I would have to always re-create the whole entity?

1
source | link

DDD different aggregates for the same model but different sagas / use cases?

I have an invoice aggregate that I create with a lot of data:

$invoice = Invoice::create(
    InvoiceId::generate(),
    $lines, // Collection of lines
    InvoiceNumber::create('1'),
    $address, // Address value object
    Currency::EUR(),
    null,
    $discounts // Collection of discounts for lines and the invoice itself
);

So this works perfectly fine when I create a new invoice or edit an existing invoice. But what if I just want to cancel an invoice?

The CancelInvoiceCommand contains just the identifier and the handler then needs to instantiate the aggregate. But I don't have nor do I need all the data above. So can I simply do something like this?

$invoice = Invoice::createForStateChange($invoiceId);
$invoice->cancel();

The problem with that is, it will use the same aggregate object that implements Invoice::create() as well. The problem I try to understand is, that when I do that, the aggregate will be in an invalid state because it has a lot other methods as well to operate on the data that is available when create() was used but not when createForStateChange() was used.

Do I have to always create an aggregate root with all available data or can I have also another aggregate that addresses the same domain but a different process / part of it? So I would have a more simplified aggregate that just relies on having the id present?