2

I don't know how to look for this so I apologize if this is already answered.

I'm wondering how to decide what is best in terms of SRP and explicit business rules.

I feel that writing business logic dependent on events is somewhat weird

For example, I have a Command Handler. The resulting system status from calling this command handler is supposed to end with a user linked in a table with an external provider ID, and generate an active subscription in the system.

Example:

google_users (google_id, user_id) -> new record
subscriptions (user_id) -> new record

Then the question is, what is the correct approach?

  1. Handler calls repository to associate Google user
  2. Repository has business rules, when this happens, we:
  3. Associate the user in google_users
  4. Create a subscription

This does too many things?

Versus

  1. Handler calls repository to associate Google User
  2. Repository associates google_users record
  3. Handler fires event (Google User Associated)
  4. Event then has listeners, one of them is Create Subscription
  5. Event executes and creates the subscription

This has somewhat "indirect" business rules through events?

Like both have pros/cons

2
  • 1
    Questions asking for pros and cons, or asking for general feedback tend to get closed as opinion-based or needing focus. This doesn't give the community a problem to solve, which doesn't fit the Q&A style of this site. Commented May 29 at 19:40
  • 2
    @GregBurghardt While I think OP has gone off the rails a bit in terms of tackling many things at once, they question is fairly direct: "what is the correct approach?" coupled with two described approaches. It's not asking for everyone's subjective opinion, the question is rooted in the idea that there is a single objective answer to this question. I do believe that the more correct answer here is a frame challenge more so than a choice between OP's dichotomy; but I do consider this question to be on-topic as OP has done their best effort to analyze the problem and explain their working out.
    – Flater
    Commented May 30 at 0:30

3 Answers 3

3
  1. Handler calls repository to associate Google user
  2. Repository has business rules, when this happens, we:
  3. Associate the user in google_users
  4. Create a subscription

This does too many things?

It's impossible to tell based on that description alone. If, for example, each one of those bullet points means ALL of the logic revolving around that down to the IO calls, yeah that's too much.

But your bullet point list here might just be calling other objects to do that work for you. In that case, this method isn't doing too much, because it's doing one thing: orchestrating the flow.

This is the core answer to your question, which is built on a supposition that one can objectively label SRP based on a generalized bullet point description without looking at the concrete implementation. It misses the point of what SRP tries to solve. This is a very common misinterpretation of SRP and it effectively causes you to fight a ghost problem that's not there, but since you believe it to be there, you spend the effort anyway.

It is unclear to me whether you are using "event" here as shorthand to mean "someone else does it", or if you specifically mean events (which get sent to any and all subscribers that aren't known to the originating class at compile time, as opposed to directly calling a method on a known instance).

Events are a way of having two (or more) classes cooperate to achieve a common goal while being loosely coupled; but it's not the only way. With that in mind, let's answer your core question:

Yes, SRP generally suggests that you break down sufficiently independent responsibilities. The knock-on effect to this is that these separate objects now have to be orchestrated to correctly interact with one another. This orchestration is an additional separate responsibility. The orchestrated interaction should be loosely coupled. There are multiple ways to loosely couple this interaction, with events being one of them, but whether you use events or another approach is irrelevant as to how and when you adhere to SRP.

2
  • Thank you, the orchestrating responsibility seems to be a good argument. My question was leaning a bit towards "if event-driven business logic is a bad idea", vs looking at the business rules being more declarative in one place. Because adding a Subscription in my case, is business rule, it MUST happen. But with events I feel like "meh, it can happen, depends on if the listener is configured". I feel like getting a clear idea of what the business rules are for a given system is scattered over all the event listeners. Is there a way to understand when something can be in a event vs sync code?
    – JorgeeFG
    Commented May 30 at 1:41
  • 1
    @JorgeeFG There's a difference between you (the developer) making sure that B definitely subscribes to A's event, and A needing to make sure that B exists and can be reached. That is essentially what you're focusing on here; but it is not the only design consideration for going event-driven or not. This requires much more literature on the subject than what I can provide in a comment or answer, I recommend looking for a book or course on the subject of event-driven systems.
    – Flater
    Commented May 30 at 2:18
1

It's OK to have an event driven system, but you have to recognise the problems in orchestrating the events.

If you have a short bit of logic like this there isn't really any reason, or at least you haven't given any reason, not to put it all in a single object that handles all the rules in one go.

That's the easiest approach and simplest to debug, find and fix errors etc.

If you do have an event driven solution, the best approach is to keep on top of the number of events you fire and make sure it doesn't grow out of control.

It might seem great to have this whole bunch of loosely coupled events all triggering each other dynamically. But it's not really changed anything fundamental about your overall program. Just instead of calling methods, you are passing messages through your event system. So you still have to worry about crashes, infinite loops, transactions, circular references etc. Just with less help from the compiler.

0

First approach:

Handler calls repository to associate Google user

Repository has business rules, when this happens, we:

Associate the user in google_users

Create a subscription

it is procedural, the request it is processed instruction by instruction, there isn't any asynchonousness involved, and the answer is returned when each and every instruction has been run. It borders all or nothing approach. It is easy to read and to maintain.

Second approach:

Handler calls repository to associate Google User

Repository associates google_users record

Handler fires event (Google User Associated)

Event then has listeners, one of them is Create Subscription

Event executes and creates the subscription

it is reactive, the request it is decomposed and dispatched to the registered events knowledgable in processing it, it offers the benefits of reactive systems (resilience, responsiveness, elasticness, message drivenness). The answer could be returned at any stage of the process. It is easy to extend and difficult to maintain (try adding business rules precedence in an event driven system).

What is the best of the two?

Scaling the solution could unclutter the clutterness. Imagine along with the subscription a user profile has to be added too.
With the first approach it translates to a new instruction calling the add user profile use case after or before creating the subscription. The time to respond to a request increases.
With the second approach it translates to a new event calling the add user profile use case subscribed to the handler. The response time is unchanged since the handler dispatches the event and sends the response to the request initiator that it will be notified after its request processing is finished.

Knowing just what has been described I tend to opt for the first approach. Knowing the entire landscape most probably changes my option in favour of the second approach.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.