Skip to main content
added 880 characters in body
Source Link
VoiceOfUnreason
  • 34.7k
  • 2
  • 45
  • 84

I don't like the need to pull all product SKUs out of the database to do the operation in-memory.

Maybe you don't need to. If you test your sku with a Bloom filter, you can discover many unique skus without loading the set at all.

If your use case allows you to be arbitrary about which skus you reject, you could punt away all of the false positives (not a big deal if you allow the clients to test the skus they propose before submitting the command). That would allow you to avoid loading the set into memory.

(If you wanted to be more accepting, you could consider lazy loading the skus in the event of a match in the bloom filter; you still risk loading all the skus into memory sometimes, but it shouldn't be the common case if you allow the client code to check the command for errors before sending).

I don't like the need to pull all product SKUs out of the database to do the operation in-memory.

Maybe you don't need to. If you test your sku with a Bloom filter, you can discover many unique skus without loading the set at all.

If your use case allows you to be arbitrary about which skus you reject, you could punt away all of the false positives (not a big deal if you allow the clients to test the skus they propose before submitting the command). That would allow you to avoid loading the set into memory.

(If you wanted to be more accepting, you could consider lazy loading the skus in the event of a match in the bloom filter; you still risk loading all the skus into memory sometimes, but it shouldn't be the common case if you allow the client code to check the command for errors before sending).

added 241 characters in body
Source Link
VoiceOfUnreason
  • 34.7k
  • 2
  • 45
  • 84

You need to read Greg Young on set validation.

Short answer: before you go too far down the rats nest, you need to make sure that you understand the value of the requirement from the business perspective. How expensive is it, really, to detect and mitigate the duplication, rather than preventing it?

The problem with “uniqueness” requirements is that, well, very often there’s a deeper underlying reason why people want them -- Yves Reynhout

Longer answer: I've seen a menu of possibilities, but they all have tradeoffs.

You can check for duplicates before sending the command to the domain. This can be done in the client, or in the service (your example shows the technique). If you aren't happy with the logic leaking out of the domain layer, you can achieve the same sort of result with a DomainService.

class Product { void register(SKU sku, DuplicationService skuLookup) { if (skuLookup.isKnownSku(sku) { throw ProductWithSKUAlreadyExistsException(...) } ... } } 

Of course, done this way the implementation of the DeduplicationService is going to need to know something about how to look up the existing skus. So while it pushes some of the work back into the domain, you are still faced with the same basic problems (needing an answer for the set validation, problems with race conditions).

You can do the validation in your persistence layer itself. Relational databases are really good at set validation. Put a uniqueness constraint on the sku column of your product, and you are good to go. The application just saves the product into the repository, and you get a constraint violation bubbling back up if there is a problem. So the application code looks good, and your race condition is eliminated, but you've got "domain" rules leaking out.

You can create a separate aggregate in your domain that represents the set of known skus. I can think of two variations here.

One is something like a ProductCatalog; products exist somewhere else, but the relationship between products and skus is maintained by a catalog that guarantees sku uniqueness. Not that this implies that products don't have skus; skus are assigned by a ProductCatalog (if you need skus to be unique, you achieve this by having only a single ProductCatalog aggregate). Review the ubiquitous language with your domain experts -- if such a thing exists, this could well be the right approach.

An alternative is something more like a sku reservation service. The basic mechanism is the same: an aggregate knows about all of the skus, so can prevent the introduction of duplicates. But the mechanism is slightly different: you acquire a lease on a sku before assigning it to a product; when creating the product, you pass it the lease to the sku. There's still a race condition in play (different aggregates, therefore distinct transactions), but it's got a different flavor to it. The real downside here is that you are projecting into the domain model a leasing service without really having a justification in the domain language.

You can pull all products entities into a single aggregate -- ie, the product catalog described above. You absolutely get uniqueness of the skus when you do this, but the cost is additional contention, modifying any product really means modifying the entire catalog.

You need to read Greg Young on set validation.

Short answer: before you go too far down the rats nest, you need to make sure that you understand the value of the requirement from the business perspective. How expensive is it, really, to detect and mitigate the duplication, rather than preventing it?

Longer answer: I've seen a menu of possibilities, but they all have tradeoffs.

You can check for duplicates before sending the command to the domain. This can be done in the client, or in the service (your example shows the technique). If you aren't happy with the logic leaking out of the domain layer, you can achieve the same sort of result with a DomainService.

class Product { void register(SKU sku, DuplicationService skuLookup) { if (skuLookup.isKnownSku(sku) { throw ProductWithSKUAlreadyExistsException(...) } ... } } 

Of course, done this way the implementation of the DeduplicationService is going to need to know something about how to look up the existing skus. So while it pushes some of the work back into the domain, you are still faced with the same basic problems (needing an answer for the set validation, problems with race conditions).

You can do the validation in your persistence layer itself. Relational databases are really good at set validation. Put a uniqueness constraint on the sku column of your product, and you are good to go. The application just saves the product into the repository, and you get a constraint violation bubbling back up if there is a problem. So the application code looks good, and your race condition is eliminated, but you've got "domain" rules leaking out.

You can create a separate aggregate in your domain that represents the set of known skus. I can think of two variations here.

One is something like a ProductCatalog; products exist somewhere else, but the relationship between products and skus is maintained by a catalog that guarantees sku uniqueness. Not that this implies that products don't have skus; skus are assigned by a ProductCatalog (if you need skus to be unique, you achieve this by having only a single ProductCatalog aggregate). Review the ubiquitous language with your domain experts -- if such a thing exists, this could well be the right approach.

An alternative is something more like a sku reservation service. The basic mechanism is the same: an aggregate knows about all of the skus, so can prevent the introduction of duplicates. But the mechanism is slightly different: you acquire a lease on a sku before assigning it to a product; when creating the product, you pass it the lease to the sku. There's still a race condition in play (different aggregates, therefore distinct transactions), but it's got a different flavor to it. The real downside here is that you are projecting into the domain model a leasing service without really having a justification in the domain language.

You can pull all products entities into a single aggregate -- ie, the product catalog described above. You absolutely get uniqueness of the skus when you do this, but the cost is additional contention, modifying any product really means modifying the entire catalog.

You need to read Greg Young on set validation.

Short answer: before you go too far down the rats nest, you need to make sure that you understand the value of the requirement from the business perspective. How expensive is it, really, to detect and mitigate the duplication, rather than preventing it?

The problem with “uniqueness” requirements is that, well, very often there’s a deeper underlying reason why people want them -- Yves Reynhout

Longer answer: I've seen a menu of possibilities, but they all have tradeoffs.

You can check for duplicates before sending the command to the domain. This can be done in the client, or in the service (your example shows the technique). If you aren't happy with the logic leaking out of the domain layer, you can achieve the same sort of result with a DomainService.

class Product { void register(SKU sku, DuplicationService skuLookup) { if (skuLookup.isKnownSku(sku) { throw ProductWithSKUAlreadyExistsException(...) } ... } } 

Of course, done this way the implementation of the DeduplicationService is going to need to know something about how to look up the existing skus. So while it pushes some of the work back into the domain, you are still faced with the same basic problems (needing an answer for the set validation, problems with race conditions).

You can do the validation in your persistence layer itself. Relational databases are really good at set validation. Put a uniqueness constraint on the sku column of your product, and you are good to go. The application just saves the product into the repository, and you get a constraint violation bubbling back up if there is a problem. So the application code looks good, and your race condition is eliminated, but you've got "domain" rules leaking out.

You can create a separate aggregate in your domain that represents the set of known skus. I can think of two variations here.

One is something like a ProductCatalog; products exist somewhere else, but the relationship between products and skus is maintained by a catalog that guarantees sku uniqueness. Not that this implies that products don't have skus; skus are assigned by a ProductCatalog (if you need skus to be unique, you achieve this by having only a single ProductCatalog aggregate). Review the ubiquitous language with your domain experts -- if such a thing exists, this could well be the right approach.

An alternative is something more like a sku reservation service. The basic mechanism is the same: an aggregate knows about all of the skus, so can prevent the introduction of duplicates. But the mechanism is slightly different: you acquire a lease on a sku before assigning it to a product; when creating the product, you pass it the lease to the sku. There's still a race condition in play (different aggregates, therefore distinct transactions), but it's got a different flavor to it. The real downside here is that you are projecting into the domain model a leasing service without really having a justification in the domain language.

You can pull all products entities into a single aggregate -- ie, the product catalog described above. You absolutely get uniqueness of the skus when you do this, but the cost is additional contention, modifying any product really means modifying the entire catalog.

Source Link
VoiceOfUnreason
  • 34.7k
  • 2
  • 45
  • 84

You need to read Greg Young on set validation.

Short answer: before you go too far down the rats nest, you need to make sure that you understand the value of the requirement from the business perspective. How expensive is it, really, to detect and mitigate the duplication, rather than preventing it?

Longer answer: I've seen a menu of possibilities, but they all have tradeoffs.

You can check for duplicates before sending the command to the domain. This can be done in the client, or in the service (your example shows the technique). If you aren't happy with the logic leaking out of the domain layer, you can achieve the same sort of result with a DomainService.

class Product { void register(SKU sku, DuplicationService skuLookup) { if (skuLookup.isKnownSku(sku) { throw ProductWithSKUAlreadyExistsException(...) } ... } } 

Of course, done this way the implementation of the DeduplicationService is going to need to know something about how to look up the existing skus. So while it pushes some of the work back into the domain, you are still faced with the same basic problems (needing an answer for the set validation, problems with race conditions).

You can do the validation in your persistence layer itself. Relational databases are really good at set validation. Put a uniqueness constraint on the sku column of your product, and you are good to go. The application just saves the product into the repository, and you get a constraint violation bubbling back up if there is a problem. So the application code looks good, and your race condition is eliminated, but you've got "domain" rules leaking out.

You can create a separate aggregate in your domain that represents the set of known skus. I can think of two variations here.

One is something like a ProductCatalog; products exist somewhere else, but the relationship between products and skus is maintained by a catalog that guarantees sku uniqueness. Not that this implies that products don't have skus; skus are assigned by a ProductCatalog (if you need skus to be unique, you achieve this by having only a single ProductCatalog aggregate). Review the ubiquitous language with your domain experts -- if such a thing exists, this could well be the right approach.

An alternative is something more like a sku reservation service. The basic mechanism is the same: an aggregate knows about all of the skus, so can prevent the introduction of duplicates. But the mechanism is slightly different: you acquire a lease on a sku before assigning it to a product; when creating the product, you pass it the lease to the sku. There's still a race condition in play (different aggregates, therefore distinct transactions), but it's got a different flavor to it. The real downside here is that you are projecting into the domain model a leasing service without really having a justification in the domain language.

You can pull all products entities into a single aggregate -- ie, the product catalog described above. You absolutely get uniqueness of the skus when you do this, but the cost is additional contention, modifying any product really means modifying the entire catalog.