I have an aggregate root, Agent, in one bounded context that is devoid of authentication concerns:
Bounded context A:
interface Agent {
id: AgentId,
firstName: FirstName,
lastName: LastName,
email: Email
}
Agents are also responsible for registering new agents. That is, an Agent is also a factory for other agents.
Whenever a new Agent is registered, an event AgentRegistered is published.
Bounded context B:
Every agent needs an Account to log-in.
This bounded context B subscribes to AgentRegistered events; whenever an agent is registered, it creates an account for it (passwords are generated).
interface Account {
agentId: AgentId,
email: Email,
password: String
}
The "issue"
It is a business requirement that emails should be unique. That is:
An Agent should not be able to register new agents that have an used e-mail.
An Account should not be created if the e-mail is already being used.
It is also a business requirement that every agent should have an account.
In bounded context A, I can simply check if there are agents with the same e-mail by querying in their repository. If not, AgentRegistered event is published.
But, what happens if, somehow, there is an Account with the same e-mail? The whole process (registering the Agent and creating its account) should be invalidated.
Possible solutions?
Make the process of registering an agent and then creating an account a saga/long-running process.
A process manager would tell the Agent aggregate to register an Agent. If successful, it will tell the Account bounded context to create an account. If unsuccessful, it will rollback by telling the Agent aggregate to unregister the agent. This is ridiculously complex for something so simple.
Make Account an Entity of Agent, instead of a separate Aggregate Root.
Agent's interface would then look something like:
interface Agent {
id: AgentId,
firstName: FirstName,
lastName: LastName,
email: Email,
account: {
// wait, no need to put email here
password: String
}
}
It also makes the aforementioned business requirement that every agent must have an account more explicit.
However, it mixes concerns; Agent should not care about authentication concerns.
Is there a better solution, or am I just overcomplicating things?
Thanks
I am assuming there could be other personas (apart from Agents) who might be creating accounts, because of which you might end up in a situation where an email address might be present in the Accounts BC but not in Agents BC.
You can do this to check for an email address across both contexts:
You publish an AccountCreated event from Accounts BC and consume it in the Agents BC to maintain a read-only Accounts model. This model can have bare-minimum data; just the email address will suffice for this requirement. The model is read-only, so you can always wipe the data and repopulate it whenever necessary.
Now you can validate for used email addresses in both the Agents repository and the Accounts Readonly model.
Note that there will be a slight lag between account creation and populating the read-only model. So you can potentially have conflicts if two different contexts add the same email address in the same instant. But if this is a rare occurrence and you can survive the occasional blip, this is the way to go.
Related
The system I want to model is an application called GetKnowSoft. The example is simple: there is a customer, a manager, and a register customer use case. Who registers the customer in the application is the manager.
I connect the manager to the client. The question is: Do I also tie the customer to the use case, even though the customer has no active role?
If the customer doesn’t directly interact with the app, it should not be an actor.
If the customer would have some interactions (e.g. enters name and address that is validated by the manager, or receives a confirmation email from the app) then it should be an actor.
I have the follow scenario:
You need to create a Request before it to become a Shop and to get a Owner Account.
So one day you register a Request.
2 days after a manager reviews and approves your Request and it means that the system have to create the Shop and Owner Account.
In my model, I thought that Request, Shop and Owner Account were 3 Aggregate Roots, but then I read that I cannot update more than one aggregate in one transaction because they may be (and in fact they are, because Owner Account is in an external authentication service) in separated db servers.
The thing is.. I still have a Request, and when it gets approved I need to create 2 aggregate roots, the Shop (with all the shop attributes, I only have some invariants with the data, like the limit of contact emails or phones) and the Owner Account.
Then one Owner Account can be allowed to edit someone else's Shop (like a collaborator)
How could I model it?
Thanks!
From your requirements, my design would be:
Two bounded contexts:
Shopping: It has two aggregates ( Request and Shop ).
Authentication: One aggregate ( OwnerAcount ).
There exists eventual consistency:
(1) Request aggregate would have a method "aprove". This method creates the RequestAproved event.
(2) Shopping BC publishes the RequestAproved event.
(3) Authentication BC is a subscriber to this event. It reacts to the event creating an OwnerAcount aggregate.
(4) The constructor method of OwnerAcount aggregate creates the OwnerAcountCreated event.
(5) Auth BC publishes the OwnerAcountCreated event.
(6) Shopping BC is a subscriber to this event. It reacts to the event creating a Shop aggregate.
Transaction creating the Shop aggregate is different from the one that created the Request aggregate.
Here's a diagram:
(Note: There's a message queue for each event type. Another option would be just one queue for all event types)
I'm working on an event sourced application following DDD and CQRS principles, which allows the posting of ads to sell goods.
There's one specific invariant that i'm trying to model which would seem to involve a bulk update of AR's, and I don't really know how to go about it.
The invariant is as such:
A Member can post an ad
A Member could be banned by an Admin
If a Member is banned, his ads must be suspended
for the purposes of the discussion, an Ad needs to have a status, as a Member can buy an item by clicking on an ad, so it's important to know if an ad is active.
I have designed my aggregate roots as such:
Member
Ad
Order
A Member can be a buyer or a seller, depending on the context, so I decorate the member object as needed.
When ads are published, they are of course inserted in a read model.
Now, when a Member is banned, there's an event associated that the Member AR triggers.
MemberWasBanned (MemberId)
My question is how do I go about finding every Ad that the member owns, and suspend them?
While I could rely on the member status for a buy transaction, it's important that the Ad tracks its status as there are other similar operations that could trigger the sending of an email for instance to the member indicating that his ads were suspended for such or such reason.
So my best approach after a lot of thinking is to create a long running process, in which I create a handler for MemberWasBanned, then go look for his active ads in the read model, and issue commands to suspend them one by one.
Am I missing something? I thought of using a process manager, but read that you shouldn't access the read side from a PM. In any case a PM in most cases determines the command sent to ONE AR.
Am I missing something?
If you have a messaging mechanism, maybe you can "explode" the MemberWasBanned event.
Publish the MemberWasBanned (or equivalent) event through your messaging pipeline, and subscribe to it from the context that handles ads. When this event is received in your messaging mechanism, you can explode it into multiple DisableAd events that will also be sent through your messaging system, each of them targetting one current ad of the banned member.
Each of these events, then, will only write on a single Aggregate (each ad, disabling it) when they're processed by the messaging mechanism.
Concurrently, the banned user will prevent further ads from being inserted, so you will be safe on that end as well.
Building an app for schools. Teachers will have classrooms assigned to them. Teachers login via email/password.
We also have district supervisors/administrators who need to easily distribute these classrooms to teachers. Currently I have a feature where they can assign a classroom to an email address, and when a user with that email signs up, it is automatically granted to them.
Is this safe? Or is it possible for two individuals to have the same email address?
Two email accounts cannot have the same email address, but whether multiple individuals are using the same address will depend on the policy of the owner of the domain, or it might be up to individual users for public systems such as Gmail or Hotmail if they want to share their address with others.
Is the app (or a particular deployment of it) restricted to only allow sign-ups from the domain of the school? (e.g. at Example College only alow emails that end in #example.edu?)
If so, you should check with the school as it is upto them how emails are distributed and used. If anyone can sign up you should be aware that people may share email addresses at certain institutions or that email addresses may be recycled for new teachers if no longer in use.
To make this approach more secure you should:
Verify the email address of each person that signs up. This can be a confirmation email containing a link with an ID generated by a cryptographically secure RNG. When the user clicks the link http://www.example.com/confirm.aspx?id=123456qwerty this will verify that they have access to the email address and grant them access to the classroom.
Expire the pre-granted rooms after a set number of days. This will reduce the chances of a classroom being granted to any recycled email address.
A couple things jump out to me.
1) You seem to be assigning resources to an account (an email address) before it has been created. How do you know that the person registering with that email address is the person that you expect?
2) You can only assume that an email address is unique if you control the domain of the email addresses and you enforce uniqueness. Otherwise it is possible that email addresses will be recycled over time (quite common with corporate email addresses like jsmith#company.com).
I'm working on a simple web service that allows users to sign up for free and upload a small amount of data. I can easily establish a quota for each user, but malicious users could create fake accounts to upload as much data as they like in a denial-of-service attack.
Obviously, there's no perfect defense against this type of attack, but what can we do to mitigate this problem?
Tie it to a more-or-less unique identifier (phone number, bank account number, facebook/google/etc account) or to a finite resource (such as time, by using a captcha).
use a captcha on account creation to ensure that it's a
human and not an automated process.
require a valid email address and require that they click a link in their email to validate that that's their email address and continue the registration process. This cuts down on their ability to create many throwaway accounts because you can limit them to only having one account per email address and they have to then create a new email address for each account they want to create.
When the user signs up, the user supplies a valid email. Most accounts are not enabled until a response has been received, usually by clicking a link in the body of that email. When that click-through is received, you should be able to grab an IP address. That should help you curtail an abundance of casual DOS attacks.
Consider Phone Number Verification
Requiring phone number for account creation is the best approach I've come across; Creating a new email or cycling an IP address is pretty trivial, but genuine sms phone numbers cost money to activate & grant your service the ability to restrict access by country-code.
An important caveat: Virtual phone numbers (like google-voice), temporary-phone number services, & burner phones can make sms-verification ineffective at preventing duplicate user accounts. Depending on your use case, it might be worthwhile to use a service, like Vonage's Number Insight api, that lets you identify those types of numbers.
Authillo is a passwordless authentication provider that prevents duplicate/fake accounts by leveraging sms verification, liveness detection, & facial recognition. Depending on how critical it is that you prevent fake accounts on your service, their base plan might be what you're looking for.
Just log the IPs and assume the same user if the IP does not change within a time interval. This is bad, because it would prevent multiple users in the same house (same IP) but it is a good start.