I've read some guides / tutorials about Symfonys event system. But I am still not sure about the naming best practice. Unfortunatelly most documentations use default scenarios like login, etc. So here is an example from a game:
A command evaluations some kind of match result. It fires an appropriate event like this:
$dispatcher->dispatch('game_bundle.match_won', new MatchWonEvent($match, $winner));
Now I want to register several listeners to handle this event, like for example one for posting this to the winner's Facebook page and another one to book an achievement for the winner. In the examples I found the listener handling the login event was mainly called something like LoginListener, but shouldn't this name relate to the actual use of it instead of the event it is related to? Because now in my example I would need a MatchWonListener, but should that contain both the Facebook and the achievement logic? That would make the event system useless then... Wouldn't it be better to have one FacebookListener with an onMatchWon($event) and one AchievementListener with it's own onMatchWon($event) method? This would also make it easy to add more Facebook-related events into the FacebookListener for example.
I am confused about the naming in the samples and not sure about now. Am I getting it totally wrong?
There's no "best practice" on how to name events. However, if you name the listener after the event, I think that defeats the purpose of events altogether. The goal is to be able to let different parts of your system interact between each other without coupling and mixing concerns.
So considering you went this far of creating events to separate concerns, why would you go and mix all the different logics into one listener? In that case you might as well just do a direct call instead of dispatching an event.
I'm personally against names like "onMatchWon" because that doesn't describe what the method does. Let's say you want to listen to a match won event and update the achievements of the user who won. I'd probably have some user manager service or the sort with a method updateAchievements(MatchWonEvent $event). But I think that's more of a matter of taste, or convention if you're willing to.
Related
In my Laravel application, I have a Model called Project which has, among others, a property called approved_at. There is also a ProjectController, which contains the usual methods *(index, show, create, update, edit...)
Now, the user that created the Project can edit it (modifying its other attributes), but only some "staff" members have the right to approve one.
From what I understand, there can be two ways to handle the approval operation:
Both users, from their respective views (let's call them edit and approve) fire the ProjectController#update method, which will internally distinguish who-can-do-what by means of authorization or policies;
I create a new ProjectApprovalController, with its own update method, and check the authorization at the endpoint (eg, /projects/{id}/approve).
What is the best approach to tackle this behaviour?
It depends on what do you want to do with this in the future. If there would be some kind of extra steps to do behind the approve method for example: connection to external micro service to check if project exists in external database with subsidies then you should definitely split it.
If you don’t mind I would suggest you to not focus so much on the implementation. Your application should be removable as fast as you build it. There is a great presentation about this from Greg Young called ‘The Art of
Destroying Software’. Be more focus to build your solution with SOLID principles and test the behaviour of this method to make it easier to replace in the future.
to answer your question, second option is more restful approach, but I don’t know if that is not shooting to fly with a cannon
I can't get my head around concept of mixing together DDD with ES. I consider events as being part of domain side. Given that there is no problem with publishing them from repository to outside world and keeping models pure and simple. But aside from that there must be possibility of replaying them back on particular aggregate. This is where my problem occurs. I would like to keep my domain models pure and simple objects that remain lib/framework agnostic.
To apply past events on aggregate the aggregate must be aware of being part of ES structure (wherefore it would not remain pure domain object). As main job of aggregate is to enfroce some bussines invariants that may evolve over time it is impossible to apply old events using aggregate API. For instance, there is aggregate Post with child entities Comments. Today Post allows 10 Comments to be added, and method addCommnet() guards that rule. But it is not used to be that way all time. One year ago user was allowed to add up to 20 Comments. So appying past events may not meet current rules.
Broadway (popular PHP CQRS library) works around the problem by applying events without any prevalidation. Method addCommnet() just checks it against our invariants and then processes appling events. Applyinig event itself does not do any further checking. That is greaat but I perceive that as high level of integration in my domain models. Does really my domain model need to know anything about infastructure (which is ES style of saving data)?
EDIT:
To state the problem with the simplest words possible: is there any opportunity to get rid of all those applyXXX() methods from aggregate?
EDIT2:
I have written (bit hacky) PoC of this idea with PHP - github
Disclaimer: I'm a CQRS framework guy.
Broadway (popular PHP CQRS library) works around the problem by applying events without any prevalidation.
That's the way every CQRS Aggregate works, events are not checked because they express facts that already happened in the past. This means that applying an event doesn't throw exceptions, ever.
To apply past events on aggregate the aggregate must be aware of being part of ES structure (wherefore it would not remain pure domain object)
No, it doesn't. It must be aware of its past events. That is good.
Today Post allows 10 Comments to be added, and method addCommnet() guards that rule. But it is not used to be that way all time. One year ago user was allowed to add up to 20 Comments. So appying past events may not meet current rules.
What keeps you aggregate from ignoring that event or to interpret differently than 1 year ago?!
This particular case should make you think about the power of CQRS: writes have a different logic than reads. You apply the events on the aggregate in order to validate the future commands that arrive at it (the write/command side). Displaying those 20 events is handled by other logic (the read/query side).
This is where my problem occurs. I would like to keep my domain models pure and simple objects that remain lib/framework agnostic.
CQRS make possible to keep your aggregates pure (no side effects), no dependency to any library and simple. I do this using the style presented by cqrs.nu, by yielding events. This means that aggregate command handlers methods are in fact generators.
The read models can also very very simple, plain PHP immutable objects. Only the read model updater has dependency to persistence, but that can be inversed using an interface.
I can't get my head around concept of mixing together DDD with CQRS.
From the sound of things, you can't quite get your head around the mix of DDD and event sourcing. CQRS and Event Sourcing are separate ideas (that happen to go together well).
Today Post allows 10 Comments to be added, and method addCommnet() guards that rule. But it is not used to be that way all time. One year ago user was allowed to add up to 20 Comments. So appying past events may not meet current rules.
That's absolutely true. Notice, however, that it is also true that if you had a non event sourced post with 15 comments, and you try to make a "rule" now that only 10 comments are allowed, you still have a problem.
My answer to this puzzle (in both styles) is that you need a slightly different understanding of the responsibilities involved.
The responsibility of the domain model is behavior; it describes which states are reachable from the current state. The domain model shouldn't restrict you from being in a bad state, it should prevent good states from becoming bad states.
In version one, we might say that the state of a Post includes a TwentyList of Comments, where a TwentyList is (surprise) a container that can hold up to 20 comment identifiers.
In version two, where we want to maintain a limit of 10 comments, we don't change the TwentyList to a TenList, because that gives us backward compatibility headaches. Instead, we change the domain rule to say "no comments may be added to a post with 10 or more comments". The data schema is unchanged, and the undesirable states are still representable, but the allowed state transitions are greatly restricted.
Ironically enough, a good book to read to get more insights is Greg Young's Versioning in an Event Sourced System. The lesson, at a high level, is that event versioning is just message versioning, and state is just a message that a previous model left behind for the current model.
Value types aren't about rule constraints, they are about semantic constraints.
Keep in mind that the timelines are very different; behaviors are about the now and next, but states are about the past. States are supposed to endure much longer than behaviors (with the corresponding investment in design capital that implies).
Does really my domain model need to know anything about infrastructure (which is ES style of saving data)?
No, the domain model should not need to know about infrastructure.
But events aren't infrastructure -- they are state. A journal of AddComment and RemoveComment events is state just like a list of Comment entries is state.
The most general form of "behavior" is a function that takes current state as its input and emits events as its output
List<Event> act(State currentState);
as we can always at an outer layer, take the events (which are a non destructive representation of the state, and build the state from them.
State act(State currentState) {
List<Event> changes = act(currentState)
State nextState = currentState.apply(changes)
return nextState
}
List<Event> act(List<Event> history) {
State initialState = new State();
State currentState = initialState.apply(changes)
return act(currentState)
}
State act(List<Event> history) {
// Writing this out long hand to drive home the point
// we could of course call act: List<Event> -> State
// to avoid duplication.
List<Event> changes = act(history)
State initialState = new State()
State currentState = initialState.apply(history)
State nextState = currentState.apply(changes)
return nextState;
}
The point being that you can implement the behavior in the most general case, add a few adapters, and then let the plumbing choose which implementation is most appropriate.
Again, separation of responsibilities is your guiding star: state that manages what is, behavior that manages what changes are allowed, and plumbing/infrastructure are all distinct concerns.
In the simplest terms: I'm looking for opportunity to get rid of many applyXXX() (or similar in languages with overloading methods) methods from my aggregate
applyXXX is just a function, that accepts a State and an Event as arguments and returns a new State. You can use any spelling and scoping you want for it.
My answer is very short. Indeed, it is event-sourcing that you struggle with, not CQRS.
If handling of some event changes over time, you have two scenarios really:
You are fixing a bug and your handler should really behave differently. In this case you just proceed with the change.
You got some new intent. You actually have a new handling. This means that in fact this is a different event. In this case you have a new event and new handler.
These scenarios have no relation to programming languages and frameworks. Event-sourcing in general is much more about the business that about any tech.
I would second to Greg's book recommendation.
I think that your problem is that you want to validate events when they are applied, but apply and validation are two different stages of aggregate action. When you are adding comment by method addComment(event), there is your logic to validate and this method is throwing event, when you reply event this logic is not checking again. Past event can not be changed, and if your aggregate throws exception with reply event something is wrong with your aggregate. That's how I understand your problem.
I haven't seen anyone doing this, so i'm bit confused if it can be good approach.
Some small system with login. And i have simple group management withing that system. I want to be able to easily set and execute some triggers depending of action and on group in which user belongs.
First idea that came to my mind is to have in groups table 4 fields for triggers, named like this:
add_triggers (when user is added to trigger this)
delete_triggers (when user is removed from a group)
login_triggers (when user logs in to be triggered)
logout_triggers (when user logout to be triggered)
Now i'm wondering how to relate that to an actual trigger code?
Should i have separated class triggers that should receive just "names" of triggers and in that class to hold all possible trigger's code? So that i'm able to execute that in some kind of a loop?
Does anyone have any other idea how to organize that?
p.s. those groups are quite an important part of a system. Most of the things should be based on them. They even has extending tables for complex type of groups.
thnx
What your looking for is Laravel Events.
I would definetly recommend making a class that the event calls rather than writing procedural style code.
Event::listen('auth.login', 'LoginHandler#onLogin');
How do you handle situation with blameable in the DDD way?
Ofcourse we can ignore some things, but i think that when entity need some tracking (creator, updater, time updated / created) it should be in the class that actually performs some actions on entity.
For example we have post and user, what whould be the correct way?
$post = new Post();
$post->create(); // here we can set some created_id and
other attributes by using mixins or traits like some fw do
Or it is better like this:
$user->createPost($post);
$user->update($post);
As for me second is better, even when we need to track changes that does not apply to post directly, for example:
$post->doSomethingWithPost();
$user->updatePost($post);
Seems like blameable just throws out one important entity - user who manages some things on entities.
Ofcourse we should not overcomplicate things, but usually when blameable is implemented, entity from which you will get id is a logged in user, that is incorrect to the bounded context.
Here it is some Blogging Context, where user of this context updates post and not some authenticated user.
Whats your thoughts on this one? Is there some similar questions that i maybe missed?
All your examples seem like they are not designed with the DDD principles in mind. The first indicator to me is the usage of a $user variable. In 99% of the cases this is too generic to really capture the intent of a given Model. I think there are hidden concepts that would first have to be made explicit. I think along the lines of RegisteredAuthor and Administrator. At least that's what I understand from:
Here it is some Blogging Context, where user of this context updates post and not some authenticated user.
Another question is how can a "user of this context" not be authenticated? How do you know who he is?
In general in an application that explicitly requires User management we normally have something like an IdentityContext as a supporting Sub Domain. In the different contexts we then have other Models like Author or BlogAdministrator holding a reference to the User's identity (UserId) from the IdentityContext. The Red Book has some nice examples on how to implement this.
To answer the question on how to track who changed something and when:
This concept is also referred to as Auditability, which in most revenue relevant parts of system is actually a must once your organization is reaching a certain size. In this scenario I actually always recommend an Event Sourcing approach since it comes with auditability batteries included.
In your case it would actually be enough to either capture the executing UserId as Metadata to the commands like WritePostCommand or ChangePostContentsCommand or use the UserId in a RequestContext object that knows about the execution context (who was sending this command, when was it sent, is this user allowed to execute this use case).
You can then, as Alexander Langer pointed out in the comments, just use this metadata inside your Repositories or Handlers to pass the information to the Aggregates that need it, or could even just send them to an audit log to not pollute your Domain Model with this responsibilities.
NOTE: Generally I would not use the DoctrineExtensions like Blameable in your Domain Model. They depend heavily on Doctrine's Event system, and you do not want to tie your Model into an Infrastructure concern.
Kind regards
I'm currently implementing the Observer design pattern and using it to handle adding items to the session, create error logs and write messages out to the user giving feedback on their actions (e.g. You've just logged out!).
I began with a single method on the subject called addEvent() but as I added more Observers I found that the parameters required to detail all the information I needed for each listener began to grow.
I now have 3 methods called addMessage(), addStorage() and addLog(). These add data into an events array that has a key related to the event type (e.g. log, message, storage) but I'm starting to feel that now the subject needs to know too much about the listeners that are attached.
My alternative thought is to go back to addEvent() and pass an event type (e.g. USER_LOGOUT) along with the data associated and each Observer maintains it's own list of event handles it is looking for (possibly in a switch statement), but this feels cumbersome. Also, I'd need to check that sufficient data had also been passed along with the event type.
What is the correct way of doing this?
Please let me know if I can explain any parts of this further. I hope you can help and see the problem I'm battling with.