2

I need some clarification on modelling a user for Identity and access domain. The user domain model has a contact information entity (entity because it is mutable), the customer can register with a phone number, but can choose to change it when needed.

The phone number once used by a customer can never be used by any other user. So the model I believe must allow querying the phonenumber table(since it is many to one with the customer, as the old numbers are deactivated and archived).

If creating a domainservice is ok, what should be the Repository as there is no aggregate identified. With these cases I have a customer(user) aggregate, but to allow querying all the users to see if the phone number provided by the customer is already been used by anyone else, what should be the aggregate, or can I write a DomainService that just can query the database directly to the phonenumber table to check for the uniqueness, am I violating any DDD principle doing so, what are the cleaner alternatives.

Somasundaram Sekar
  • 5,244
  • 6
  • 43
  • 85
  • 2
    http://stackoverflow.com/questions/31386244/cqrs-event-sourcing-check-username-is-unique-or-not-from-eventstore-while-sendin – Matt Oct 09 '15 at 08:53
  • @Matt, Thanks, I understand that you advise inserting the relevant data in a non domain table, optimized for read and do the validation from the client side reading from the table, but how can the server side validation be done, using a domainservice that directly query the table? – Somasundaram Sekar Oct 09 '15 at 09:41
  • 1
    As one of the answers to the other past indicate, there's a few ways to tackle this. Either keep a small read model of registered phone numbers for the write-side to refer to and perform validation against that as you execute the command, or (my preferred way) would be to just let it happen and have a process manager spot the duplication/broken validation and take a corrective action. A client should always expect any commands that it issues to be executed, so you are really talking about a consistency breach in a tiny fraction of cases. – Matt Oct 09 '15 at 10:00

2 Answers2

3

An alternative would be to create an Aggregate that makes it explicit in which scope you intend the unique constraint to hold.

As a (contrived) example, a phone number might be unique across a country but not internationally. Thus :

// An Aggregate Root
public class Country {

  // Store a lookup structure (userId, phoneNumber) here

  public void addUser(userId, phoneNumber) {
    // check phone uniqueness here
  }

  public void changeUserPhone(userId, phoneNumber) {
    // check phone uniqueness here
  }
}

Since you're using CQRS, phone numbers being in a separate Aggregate doesn't matter because on the Query side, Read Models will reassemble the User and their phoneNumber together.

This also plays well with the "don't create Aggregate Roots" approach since you have a starting point from where to create your User (User is probably an AR) and not just create it out of thin air.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • That implies `Country` has to be modified in the same transaction as `User` when the `phoneNumber` is modified no? – plalx Oct 09 '15 at 11:31
  • That reminds me a question of mine: https://groups.google.com/forum/#!topic/dddcqrs/op-PllxkkI8 – plalx Oct 09 '15 at 11:39
  • @plalx Not necessarily. If `PhoneNumber` has to be unique across a significant part of the system, it probably means that it's an important domain notion. Therefore modifying it is not a benign action, it could have its own ubiquitous language term and own command that's always carried out on its own and never as part of a normal `User` modification. – guillaume31 Oct 09 '15 at 12:22
  • Could you expand a bit more on that, perhaps with how you handle the creation of a user associated with a phone as well as how you would handle a user's phone modification. I'm having a little trouble to see the flow of all the components that will be at play. – plalx Oct 09 '15 at 13:00
  • Creation : On the client side form, there would be phone number validation as-you-type, probably querying CountryRepository or a read model containing a list of already taken phone numbers. Then you have to bring it (and revalidate it) on the server side. A CreateUser command would be issued, or a CreateUserPhone and a CreateUser one. Transaction wise it could be done in one transaction spanning the Country and User aggregates, or in 2 transactions with a compensating action if the phoneNumber part fails. – guillaume31 Oct 09 '15 at 14:32
  • The idea is that concurrent access/contention is not really a problem since we have one Aggregate that doesn't exist yet (User) and another that is probably not modified that much (only every time a phoneNumber is assigned) – guillaume31 Oct 09 '15 at 14:33
  • Modification : There could be a separate UI for changing your phone number. This paves the way for other stuff such as sending a verification SMS. Again client-side unique check here. Then `ModifyPhone` command sent that affects only the `Country` aggregate. – guillaume31 Oct 09 '15 at 14:36
  • Ok that makes sense. One thing about the compensating action though. What if 2 transactions are used but the phone failure occurs before the user was created. At this point the failure handler wouldn't have anything to correct. Would the failure handler check for user existence and if it's not existing then would register to a UserCreated event to defer the compensating action? – plalx Oct 09 '15 at 14:36
  • It depends on your domain rules. The compensating action could be as dumb as still creating the user but redirecting to a "Please re-enter your phone number" page. Or, the 2 transactions could have a causation link, for instance the second one could be triggered by the Country Aggregate raising a `User[Phone]Created` event only if PhoneNumber was properly created. There are tons of options here. – guillaume31 Oct 09 '15 at 14:44
0

You could have your repository check if the phone number exists, if it does then throw a rule exception else save the change. The key here is to inject an instance of the repository through the application layer and run rule inside the domain layer.

Flexo
  • 87,323
  • 22
  • 191
  • 272