I’m working on designing a shopping cart system that respects the single responsibility principle. However, I’m facing a challenge when it comes to handling cart creation and updating separately.
When a user logs into the website and adds products to the cart. Currently, the “add to cart” operation also implicitly creates a cart, which violates the single responsibility principle. I want to find a solution that respects the single responsibility of creating and updating the cart separately, without requiring the user to explicitly create the cart.
I’m considering a few approaches and would appreciate insights or alternative suggestions:
Approach 1: Domain Logic inside “Add to Cart” Service
If I, as the domain owner, decide to embed the cart creation logic within the “add to cart” service, allowing it to create the cart if it doesn’t exist. it seems like a practical solution, but maybe not a good design.
Approach 2: Two Separate Services
Creating two services, one for cart creation and another for cart update seems like a clean separation of concerns. The create cart service would be invoked asynchronously via an event triggered by account creation. However, I’m concerned about potential overengineering, and the asynchronous event might fail and/or take some time to be processed, affecting the user experience.
Approach 3: Two services with Observer pattern
I’m considering using the observer pattern. After the user creates an account, a synchronous trigger would invoke the create cart service. Would this be a more straightforward solution in terms of implementation and user experience?
Never use SRP to this kind of degree. The SRP gets a fair amount of hate, specifically because of cases where it’s used to such a granular degree that it becomes more a problem/obstacle than an actual solution or helpful guideline.
Simply put: no, creating a cart when none existed is not a violation of SRP. It’s merely a violation of your overly strict interpretation of SRP. SRP was originally coined on a much higher level than this, e.g. if you were processing payments with a payment provider inside of the
Cart class. You are nowhere near that kind of SRP violation.
It would be much more helpful in this instance to think of it in terms of reusability. It will automatically help you adhere to SRP. Note the following:
- When you add the first item, you create a cart and then add the item to the (now) existing cart.
- When you add further items, you add the item to the existing cart.
What you should conclude here is that in both cases, the logic for adding an item to a cart is the same, and therefore it should be reused. However, there’s multiple ways to skin this cat.
The two main solutions are either to use a separated create/update use case, or to simply use cart creation as a small extra bit inside the “add to cart” use case. Both are valid solutions.
- If you expect there to be more differences between the two use cases, keep them separate.
- If you are confident that all your behavior can adequately be described as a single “add to cart” use case, then do that.
The short answer here is that you should just drop your current SRP focus and instead focus on your requirements. This isn’t about right/wrong, this is about what your application requirements are and modeling your system to best implement those requirements.