I could almost agree with king-side-slide's answer.
The uniqueness of an entity is not a business rule!
I say "almost", because this is not absolutely true when you define systems, meaning that you are free to impose any rule you like, as is common in DDD. However, when designing/abstracting a system, a business rule does not automatically make sense just by simply "wording" it. The problem stems from the fact that one may not be staring at their Model as a conceptual system in its totality, so some business rules may not be "worded" properly. Stating a business rule is one thing, but clarifying the interactions hidden behind the rule is another. Too simplified "wordings" usually convey too little information.
For starters, think about it in a simplistic manner...
Let's consider an entity: User
, with various properties/attributes.
Let's introduce a Rule: Users must be unique.
The rule practically introduces a constraint. This constraint is not free of assumptions and definitely not a domain consideration at the same level with User
entities. Think about it for a moment. "Users must be unique" automatically introduces an additional level of hierarchy! If users have to be unique, then, by definition, there are multiple users.
Ooops... we skipped another consideration first... There exist multiple users. So, you need a user repository, you may think, but do you? The answer here is "it depends". And at about that point, you start wondering... what am I talking about?
I will make the statement that if your users do not interact with each other or with some other system, then you don't need a repository, plus the uniqueness rule is completely irrelevant! Put in simple yet relevant words, users residing in different countries (or even cities) do not need unique phone numbers.
But...
If your users coexist in, or co-interact with, a system, then the system has to be a higher-order entity than users, plus it has to "know" the users somehow (typically by proxy identifiers, such as records). For example, a hospital contains medical records, and user duplicates must not exist. So, you have your uniqueness constraint, but you also have a hospital. See, where I am going with this? First, there is a hospital (a hierarchically higher-level entity) and then, there are User
entities, representing, for example, the patients.
Once we get past the fact that uniqueness (as a constraint) is never imposed at the same level with the entities "suffering" from it, things become much clearer. The uniqueness constraint is there and it is real, but it is not a problem of the entities. In a sense, all "constructed" entities are unique anyway (both in the material world, and in the digital world, think about how you always use some kind of "new" statement to instantiate an object). It is the problem of that other higher-level entity/system that has to maintain proxy-records with respect to the lower-level entities. It is solely its own problem to make sure, not that entities are unique, but that its records, the ones that represent entities, are unique, in a sense that has nothing to do with the entities themselves.
Therefore, uniqueness is an artifact, indeed, and an implementation-detail relating to persistence. It is best implemented at that layer.
From then on, you just need to ask yourself again, what it is you are trying to achieve, as each specific problem has its own set of advisable solutions. For example, why do you want to have unique phone numbers? This constraint is unenforceable at the users themselves, who are going to be using your own system. That is because in most real-world models, users don't choose their phone numbers, they are assigned to them and, until assignment and upon assignment, phone numbers are simply artifacts, just like unique IDs used by databases. This ensures that, upon creation, phones are unique anyway.
However, you may have valid reasons for this, for example you might be registering "per-household", or you don't want a user registering using the phone of another user. But then, your Model should be clear. You do want uniqueness, of sorts, and you (should) know why you need it. In that case, your higher level system entity, the one "containing" the users (their records, actually), being the nicely-designed abstraction it is, will contain a Map
of user-records "keyed" by various elements, with the phone number being one of the keys.
Think about this abstraction:
class PhoneNumber
{
//Make sure this looks like a proper phone number,
//i.e. constructed strictly by 10-digit numeric literals.
}
class ISP
{
Map<PhoneNumber, User> UserRecords;
}
Can anyone miss the fact that Phone numbers must be unique for each user registered to the ISP when they come across this somewhere inside their Model? I presume not. Plus all the relevant information is there, in a single location.
In (very) short, the problem usually arises when not having put that much thought into what type of system is being abstracted. You may think about the uniqueness of your entities, but you are better off thinking about what purpose this uniqueness serves, and which (hierarchically higher-level) entity's responsibilities it falls within.
Also, because I have not stated the alternative I started with in mind, given that your problem is this:
In most databases it's fairly easy to add a constraint that generates
an error when trying to store a duplicate value. But when following
that approach, the model doesn't explicitly make clear that a phone
number should be unique. If the model is reused or the database is
changed, these business rules could be overlooked.
...then you appear to have primarily a problem of practicality (i.e. make sure maintainers do not forget to check for rule enforcement). So an acceptable alternative would be to just use some type of meta-data or Apps Hungarian-style notation to achieve this. For example, think about the following:
class User
{
//...
[UniqueField]
PhoneNumber PhoneNumber { get; }
//...
}
or
class User
{
PhoneNumber UniquePhoneNumber { get; }
}
Yes... if someone stumbles upon this piece in some codebase, they will almost immediately get the point (I see, user phone numbers must be unique, this is a business rule), and they will probably remember to double-check the persistence layer for enforcement, but they will never know why, plus they will hardly get any idea about where this is relevant, which are the problems I have (probably over)stressed above. So, to conclude almost as I started...
Stating a business rule is one thing, but clarifying the interactions
hidden behind the rule is another.
So you may simply need to better model the User
phone number uniqueness inside the entities where it is directly relevant.
So, where does this leave us? Well... you asked:
In a hypothetical system that handles adding users, there are several
business rules. Some of the rules can easily be checked in the model.
For example a user registration can only be saved if they entered a 10
digit phone number.
But what if we want this phone number to be unique?
...if you share more information about why, and what purposes this is to serve, so that we can see the hidden interactions that this business rule entails, it might be possible to suggest more specific amendments to your Model, so that the rule can be properly enforced and immediately made clear to maintainers. Without looking at your Model as a whole, all I can suggest is something along the lines of:
class User
{
//...
[UniqueField]
PhoneNumber PhoneNumber { get; }
//...
}
This almost "screams" to the maintainer: "Wherever you store the users, make sure their phone numbers do not overlap".