I'm developing a monolithic application in which 3 types of accounts can live : Admin, Teacher and Student.
They all need an authentication mechanism but expand to support various types of operations (some operations are reserved to teachers, other to students).
My concerns is about creating a new account. I've devised two potential solutions so far.
Using Two Aggregates (
Account
for the auth mechanism, and eitherStudent
,Teacher
orAdmin
for the account type and associated actions) but that would imply creating two aggregates in a single transaction. It's common wisdom to avoid updating two aggregates in a single transaction, but there's nothing said about creation.Nesting Entities into the Account object, one per type of account. Hence, the Account aggregate would have an entity Student, Teacher and Admin, 2 of which would be set to null and only one bound to an existing entry. It would act as some form of discriminated union and would prevent me from creating multiple aggregates at once, but it feels kind of weird to deal with this kind of AR.
-
Do all e.g. students have the same permissable operations? If so, you don't need to re-create the operations each time you create a new student.Mehdi Charife– Mehdi Charife2024年04月18日 12:58:30 +00:00Commented Apr 18, 2024 at 12:58
-
Yes, every student have the same permissions (modulo the fact they one student can't touch the data of another student, but that's not role related)ancyrweb– ancyrweb2024年04月18日 15:45:20 +00:00Commented Apr 18, 2024 at 15:45
2 Answers 2
but that would imply creating two aggregates in a single transaction
Not really. It is a matter of convention: these two aggregates can be combined into a single aggregate. In other words: just redefine the term and you will be fine.
It is a good idea to have auth separated from the actual business account. So your design can look like this:
There can be more data under each kind, and the schema can be per kind. In other words, Admin
doesn't have to hold the same data as other kinds.
Moreover, perhaps Auth
can be a tree as well. Maybe in the future you will decide that you want multiple different authentication mechanisms, why not?
It would act as some form of discriminated union
Correct. All you need is (kind, object)
pair, where object
holds the "per kind" data. Then you don't have to deal with 2 of which would be set to null
issue.
I don't know which language you use, but for example Rust supports such discriminated unions with additional data out-of-the-box. Regardless, I'm pretty sure this can be emulated in any language. You can use inheritance for that as well, although it is less convenient than enums imo (because enums cover all possible choices, without the needed of understanding entire source code).
but it feels kind of weird to deal with this kind of AR.
Well, I don't see anything weird about it. Actually it looks very natural to me.
The way you described this I think Admin
, Teacher
and Student
need a common base class.
When Admin, Teacher and Student are always used as accounts in your system (which the first sentence in your question implies), then make
Account
this base class. This can be a correct usage of inheritance - an Admin/Teacher/Student is an Account, and they could also be called AdminAccount/TeacherAccount/StudentAccountWhen you want to separate the aspect of
Admin
,Teacher
andStudent
being persons from the account, then introduce a common base classPerson
for them, and modelPerson
as an entity having anAccount
.
The second approach will allow to create a new person in one transaction within the system, create a new unassociated account object - second transaction, and then associate the account to the person - third transaction. You will have to make the application expect persons without accounts and/or accounts without persons, at least temporarily.
The first approach will keep everything in one aggregate and may be simpler to implement. When you expect this to be sufficient for your app, I would recommend to use it as your first choice.
-
I'll go with the first approach, but the second approach got me curious. If I understand correctly Person & Account would be 2 different aggregates synchronized under eventual consistency. But Creating a person doesn't involve a password, which is required for accounts. How would I pass that password (provided by the API call) to that second aggregate ? I'd need to embed it inside an event, which is not directly related to person creation. It sounds weird to me.ancyrweb– ancyrweb2024年04月18日 15:52:32 +00:00Commented Apr 18, 2024 at 15:52
-
@ancyrweb: I have some trouble understanding your scenario description (probably it is too terse). Moreover, the comment sections here are not well suited for such a discussion. If you really want an answer, try asking a new question.Doc Brown– Doc Brown2024年04月18日 15:58:56 +00:00Commented Apr 18, 2024 at 15:58