Does everything have to be an aggregate? Many-to-Many Link - aggregate

Say I have two entities
Notifications and Users.
I want to mark that a user has seen a specific notification.
This would commonly be done with a many-to-many relationship
e.g. UserNotification
Because there is no invariant around this relationship (we don't care if "all" users have seen the notification) these users shouldn't be on the notification aggregate.
On the opposite side, the users aggregate doesn't need a list of notifications on it
So that leads to say that the UserNotification (this relationship) is an aggregate of its own.
However, because we are never going to reference this thing by Id, does it really really belong as one? It seems like just adding an aggregate for storing the data.
What should I do here?
Just make an aggregate anyway and ignore the id?
Put these notifications on the user or users on notifications. (does it belong on either, and would putting it on one not add weight and cause concurrency issues?)
just make a crud table?
An aggregate without the id and keep the composite key (is that allowed?)
thanks

Does a Notification have its own lifecycle? Can a Notification exist without a User to be notified?
I could imagine a Notification to simply be a Value Object that gets copied to each affected User.

have you considered modeling User and Notification as aggregates but NOT modelling the association at all?
There is a high probability of not needing to. The only usecase I can come up with is retrieving all notifcations of a user. this can be exposed in an repository interface via getNotifications(user: User): Iterable[Notifications] (scala syntax).
on the write side the saveNotification(notification: Notification, users: List[User]) could save the aggregate as well as populate the n:m table.
EDIT: on afterthought to this - my solution would introduce a source code dependency from notifications to users (at least on the repository) and your intiuition might be right - the notification should not know about the user at all.
But there has to be at least the concept of an Recipient which may perfectly reside in the notification "module" or "package". maybe you are crossing bounded contexts here and the User entity on one side should be translated to an Recipient value object on the other via Anti Corruption Layer.
It's up to you and your domain to decide. In this example it would perfectly make sense that the notification package has some knowledge about a "User". otherwise - what would be notified?

Related

Clean architecture and user logins in Flutter - how do I store user information?

I've been trying to use Reso Coder's Flutter adaptation of Uncle Bob's Clean Architecture.
My app connects to an API, and most requests (other than logging in) require an authentication token.
Furthermore, upon logging in, user profile information (like a name and profile picture) is received.
I need a way to save this data upon login, and use it in both future API requests and my app's UI.
As I'm new to Uncle Bob's Clean Architecture, I'm not quite sure where this data belongs. Here are the ideas I've come up with, all involving storing the data in a User object:
Store the User in the repository layer, in an authentication feature directory. Other repository-level methods can pass it to the appropriate datasource methods.
This seems to make the most sense; other repository-level methods that call other API calls can use the stored User easily, passing it to methods in the data source layer.
If this is the way to go, I'm not quite sure how other features (that use the API) would access the User - is it okay to have a repository depend on another, and pass the authentication repository to the new feature repository?
Store the User in the repository layer, in an authentication feature directory. Other (non-login) usecases can depend on both this repository and on one relevant to their own feature, passing the User to their repository methods.
This is also breaking the vertical feature barrier, but it may be cleaner then idea 1.
For both these ideas, here's what my repository looks like:
abstract class AuthenticationRepository {
/// The current user.
User get currentUser;
/// True if logged in.
bool get loggedIn;
/// Logs in, saving the [User].
Future<void> login(AuthenticationParams params);
/// Logs out, disposing of the [User].
Future<void> logout();
/// Same as [logout], but logs out of all devices.
Future<void> logoutAll();
/// Retrieves stored login credentials.
Future<AuthenticationParams> retrieveStoredCredentials();
}
Are these ideas "valid", and are there any better ways of doing it?
I see another option to tackle the problem. The solution I want to talk about comes from the domain-driven design and is an event based approach.
In DDD you have the concept of a bounded context. A business object (uncle bob's entity) can have different meanings in different bounded contexts. Take a look at your user business object. The data and methods that some use case uses is often differnt to the data and methods that other use cases use. That's why you have differnt user objects in differnt bounded contexts. They are a kind of perspective that each use case has on the same business object.
If a business object is modified in one bounded context it can emit a business event. Another feature can listen to those events. The event mechanism can either be a simple observer pattern or if you need to distribute your application features via microservices a message queue. In case you use a simple observer patter the event emitter and event handler can run within the same data source transaction. But they can also run in differnt ones. It depends on your needs.
So when the sign-up use case registers a new user it emits a UserSignedUpEvent. Other features can now listen to this event. The event carries the information of the user, like the email, the name, the profile image and other infomration that the user provided during sign-up. Other features can now save the piece of data they need to their own data source. It can be the same as the sign-up use case uses (just other tables or another schema). But it is also possible that is a completely differnt data source, maybe another kind of data source like a nosql db. The part I wrote above about transactions is of course more difficult if you have different data sources.
The main point is that each feature has it's own data and manages it. It might be a copy of the whole user infromation, but in a lot of cases it is only a subset.
The event-based approach can give you perfect modularization. But as it is always when something looks great, it comes at a cost. You have to duplicate some part or even all data. When you think of a microservice architecture and some features are in different microservices it means that the duplication increases the availability of the service. The service can operate even the main service that manages the data is down, because a local copy exists. But now you have to deal with consistency issues - eventual consistency.
At this point I like to stop and guide you to other sources for details:
Chapter 8: Domain Events, Implementing Domain-Driven Desing, Vaughn Vernon
The many meanings of event-driven architectures, Martin Fowler

Sharing Aggregates Between Microservices

I posted a question earlier about how notifications, and users seeing those notifications, could be modelled in DDD.
Link Here: Does everything have to be an aggregate? Many-to-Many Link
For a brief summary of this:
We can raise notifications in the system that we want to show users.
(A notification will target certain users, which you define a filter for. E.g. only show admins, only show normal users, only show users for x client)
When a user sees a notification we want to mark it so they don't see it again.
A suggestion was made on the post to have a notification aggregate and store the reference to it in the User aggregate.
So when a notification is created, the event will be picked up, and a service will add that notification to the users it seems fit.
So we have a notification list in the user.
I think this is a bounded context (a notification bounded context). Certainly if i was modelling it as a microservice, I would handle this notifications stuff in its own microservice.
If we were to use microservices, the user created event would come from another service (a users service).
Question:
The notification creation would go in the notifications microservice. I would also be tempted to put the user marking that they have seen a notification in that service as well.
So at this point, the notifications microservice wouldn't hold a full aggregate of a user, it would only have a partial, containing the; id, collection of notifications and any criteria might want to filter from
Is it ok to have an aggregate (be it a partial one, as it is only the stuff we want) in a microservice (notification microservice) that it isn't owned by?
So essentially we have an aggregate of the user in the users microservice and the notifications one.
This doesn't sound too bad as it is going to reduce the footprint on the user, by splitting parts up and it is nice to bundle this functionality into the service that handles it.
However do we want to keep all the user stuff in one place? even it it puts other functionality in with it? Would the seeing of a notification go in the user microservice (that feels wrong)
Thanks
In short, don't share the aggregates, share projections of those aggregates, which are value objects representing aggregates from other bounded contexts.
Is it ok to have an aggregate (be it a partial one, as it is only the stuff we want) in a microservice (notification microservice) that it isn't owned by?
What you call a "partial aggregate" I would call a projection of an aggregate owned by a separate BC. So this projection can be owned by the BC exposed in the importing microservice. In this sense, yes, it is ok.
So essentially we have an aggregate of the user in the users microservice and the notifications one.
No, you don't. You have User in its BC and you have a projection of the User in the Notifications BC. A BC can own the projections of aggregates from other BCs but not the foreign aggregates themselves. You only want to project what you need from the other BCs, not everything. If it was everything, then you've pretty much broken some fundamental DDD. And from a physical perspective, you might be tempted to share databases and so on, which defeats some of the hallmarks of good microservice architecture.
Question: The notification creation would go in the notifications microservice. I would also be tempted to put the user marking that they have seen a notification in that service as well.
I think this would be OK. It sounds like it from the context of your question (I certainly don't know the whole of what you're doing). Perhaps in your Notifications BC, you have a NotifiedUser with a list of Notifications or perhaps it's the other way around SeenNotifications with a list of Users.

CQRS can query be source of event?

Usually when talk implementing CQRS it is supposed that commands are sorces for events. But can queries made by user be source of created events in event store? Or such actions (when we need an event that reflects query) should be implemented using command still?
But can queries made by user be source of created events in event store?
Go not to the elves for counsel, for they will answer both no and yes.
So, the "no" part: queries are distinguished by the fact that they don't change the domain model. In a CQRS implementation, the queries are being served by the read model, which may not even have access to your event store at all.
when we need an event that reflects query
The yes part: there's no law that says you can't assemble a history of queries, and stick that in your event store.
But I'm stumped, in that I don't see a clear case where the domain needs an event that reflects a query. That's really weird. My guess would be that needing an event that reflects a query is a hint that your model is broken.
You may be able to make some progress with this by exploring the source of the requirement.
If the requirement is coming from operations, analytics, reporting, usability... then the domain model probably isn't the right place for that information.
If the requirement is coming from your domain experts ("we need to capture these queries so that the model supports the right changes later"), then you should be looking to identify what entity is responsible for tracking that the query happened, and sending an appropriate command to that entity.

design issue when too many derived classes are needed

I am trying to build a badge system that is similar to StackOverflow in my entity-framework code-first application.
I will have around 10 badges in total and each type of badge has its own properties. I am thinking to have base class Badge and derive the other classes from the base class.
For example, there will be Sprinkle badge and it will be automatically assigned to the user if his post is liked 3 times or more. So, I will have Sprinkle class with additional property NumberOfLikes (so that it can be updated later). However, in the database table, there will be only one record for this class. Isn't this weird?
I will have 10 classes like this, and there will be only single record for their corresponding table in the database. I have to have separate classes for each to be able to configure their unique properties.
Is my design choice a poor one?
You may consider to differentiate between the business rule of the badge, that is coded in the derived badge class and the bookkeeping of earned badges of a user. I see no need to have this rule classes be entity framework classes and to store them in the database if it does not have any parameters you want to change frequently. You could store this parameters in other configuration stores (eg. exe.config) or similiar or hardcode it. The (perhaps only one) instance of this classes are purely to execute the business rule. See them as services (DDD) or perhaps strategy pattern or (for evaluation) visitor pattern. They could just be created (perhaps per IoC/Di container that automatically creates all derivations) without storing/loading in the database.
On the other hand you have to bookkeep which user earned which badge (it could be a performance hit to calculate this new on every request). Here it makes sense to have a class that is stored in the database (1:n) and stores the list of badges a user received. So after every change (new post, new like, whatever) or from time to time (nightly run) you run through your badge rule classes and every badge the user does not already have (or if it is possible to loose a batch you take all) is executed for this user to check if it applies. If yes, the marker is created that the user earned the badge.
In your example, you may store in your database the user, his post and the number of likes. The Sprinkle badge could be a result of some business rules (is your post (or user) Sprinkle compatible) and may not be stored in your database?
In other worlds, the Sprinkle badge is a way to see a post (or a user) having more than 3 likes?
Maybe this rules could be stored in your database and parametrized?

Making Catalyst calls from the model?

I'm using Catalyst with Catalyst::Plugin::Authentication and
Catalyst::Plugin::Authorization::Roles and am wondering if there is a better
approach to adding an attribute to a model that I'm not seeing.
Each user is permitted to access one or more companies, but there is
always one primary (current) company at a time. The permitted list is
stored in the database, and database access is primarily through DBIC.
My first inclination is to say that it's the user that has a current
company, and thus put it as part of the user model: give the user
package a "sub company { … }" to get/set the user's current company. The
database check is fairly easy; just use "$self->search_related" (a DBIC
method, inherited by the user model).
The problems I run in to are:
The current company needs to persist between requests, but I'd rather
not store it to the database (it should only persist for this
session). The natural place is the session…
There is a role, akin to Unix's root, that allows you to act as
any company, ignoring the list in the database. Checking this role
can be done through the database, but everywhere else in the app uses
$c->assert_user_role and friends.
I've heard its best to keep the models as Catalyst-independent as
possible. It also seems pretty weird to have a model manipulating
$c->session.
Of course, I could move those checks to the controllers, and have the
model accept whatever the controller sends, but that's violating DRY
pretty heavily, and just begging for a security issue if I forget one of
the checks somewhere.
Any suggestions? Or do I just shrug and go ahead and do it in the model?
Thanks, and apologies for the title, I couldn't come up with a good one.
The key is to create an instance of the model class for each request, and then pass in the parts of the request you need. In this case, you probably want to pass in a base resultset. Your model will make all the database calls via $self->resultset->..., and it will "just work" for the current user. (If the current user is root, then you just pass in $schema->resultset("Foo"). If the current user is someone else, then pass in $schema->resultset("Foo")->stuff_that_can_be_seen_by($c->user). Your model then no longer cares.)
I have some slides about this, but they are very outdated:
http://www.jrock.us/doqueue-grr/slide95c.html#end
(See the stuff immediately before and after, also.)
Note that restricted resultsets and web ACLs are orthogonal. You want to make the model as tight as possible (so that your app can't accidentally do something you don't want it to, even if the code says to), but various web-only details will still need to be encoded in ACLs. ("You are not allowed to view this page." is different from "You can only delete your own objects, not everyone's". The ACL handles the first case, the restricted resultset handles the second. Even if you write $rs->delete, since the resultset is restricted, you didn't delete everything in the database. You only deleted the things that you have permission to delete. Convenient!)