In this example, the database does not depend on the application -- and it would be smelly code if it did. Rather, you've abstracted the database into an interface and now inject the appropriate concrete implementation into the application, instead of constructing the concrete implementation in the application. Dependency inversion is almost always more appropriately phrased as dependency injection -- for exactly the reasons in this post (you've injected, but not inverted, which is exactly the right thing to do here).
I note that this example is also a great demonstration of the single responsibility principle, which you explain in the text (what if you want to change to a different database implementation?).
That's a great example, complex enough to make the point, small enough to keep me engaged :)
If you compare the 2 versions of the SetProfileImage classes the second one is almost self explanatory.
Thanks! I don't see where you indicate to use the s3 implementation of the interface, but I imagine that happens typically in laravels service provider? How would you do that if the implementation choice (s3 vs g cloud) had to come from a database value?
That's exactly how I do it, use Laravel's service provider to bind the interface to the implementation.
As to your question, if you needed to choose the implementation based on a config detail (database value in your example), you can do that in the service provider, binding the appropriate implementation choice based on the value.
You could also make an implementation of the interface that chooses which other implementation to use based on the database value, sort of like the proxy pattern, proxying the calls through to the appropriate concrete implementation.
ok, thanks, that makes a lot of sense! I always had trouble thinking through that second part of having configuration come from database values.
Thanks for interesting article!
A minor mistake in class SetProfileImage:
private $image_store; // Is this $image_repo?
Also, I have another question, in class EloquentUserRepostiory you are using Domain\User and function get return Domain\User, is it an Eloquent model?
If it is an Eloquent model, how do you implement the repository with MongoDB, which isn't supported by Eloquent?
Maybe, we should use Entity instead of Eloquent Model?
Thanks for pointing that out, I'll correct it shortly.
You are correct, the above get call would return an eloquent model, which isn't ideal. If I were to take this code example further, I would create a domain class for User that is completely independent from Eloquent, and then translate from it to the Eloquent model inside the repository.
There is a typo in one of the snippet: $this->image_reoo = image_repo; -> $this->image_repo = $image_repo;
$this->image_reoo = image_repo;
$this->image_repo = $image_repo;
Thank you for the article!
Good spot, thanks for pointing that out. I've updated the code snippet.
This is a nice example of SoC, yet also yields to an anemic domain model when there exists a service layer operating on entities - the changes to which are not encapsulated within the entity (read: aggregate root in the case of a user changing their profile image here). ADMs are okay for CRUD, just as a rich domain model also has it's trade-offs, but I feel it's important to make the distinction clear. Purporting to use DDD yet only focusing on placing code in different directories is not DDD, it's a directory structure.
I agree, the above isn't a solid example of DDD, and it should not be viewed as such.
The above code is a contrived example to explain the concept of a clean architecture, so it doesn't feature any of the richness that a real domain model would contain.
Devs used to say this was microsoft and java concern (ddd) etc. But this is how to do it. Enjoyable piece
How would you apply clean architecture with functional programming style ?
Is it possible ?
I think it's entirely possible. Functional programming doesn't have objects, but you can still compose functions in the same way you compose objects. This means you can build high level functions (domain/usecase layers), that internally use lower layer functions that interact with infrastructure, in the same way that you'd inject services in the article above.
Excellent article, very succinct! I will share this with my team.
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.