3 ms·
This kind of thinking bothers me, but I'm not sure how to put my finger on what exactly it is that bothers me, or how to put it into words. The thing with thes
by hasenj 10y ago
This kind of thinking bothers me, but I'm not sure how to put my finger on what exactly it is that bothers me, or how to put it into words.
The thing with these abstract architecture principles is they are not always practical if you try to be puristic about enforcing them.
I get where these ideals come from. I've seen novice programmers take a stab at writing mildly complicated apps, and the code is a nightmare to read because everything is jumbled up together. This is bad and we can all agree on that.
But I've also seen projects where everything is split into a tiny little function or object that don't seem to be doing anything meaningful. Presumably these projects are following the principles of "single responsibility" and "loose coupling", but it's so loose that it's hard to put the pieces together in your mind and nearly impossible to follow the flow of the program.
I consider generic rules of the form: "<X> related objects should not be doing <Y> related stuff" to be harmful. (An example of such "bad rules" can be found in the article under the heading "Use Cases")
Instead I prefer practical rules that make the code easy to read, understand, and updated, without making it any more complicated that it needs to be.
* If a piece of logic can be contained in a function, let it be contained in one function without splitting it across 10 different objects and factories and coordinators. Even if that function is slightly long, there's no need to split it apart just because it's over the arbitrary threshold of say, 25 lines.
* Create abstractions around a set of vocabulary that you can use to describe the problem domain and the process of doing things within the application. Make sure to document your vocabulary well and try to keep it as small as possible (but not smaller)
A good example of this is git's vocabulary for commits, trees, and blobs.
Every operation doesn't need to be a class (or worse, a series of classes and factories). Operations can be functions that operate on the structure's (objects) you've defined.
* Keep related files and functions together.
If you have a server side module implementing an json API, and an html page designed to display the API, and a javascript module designed to drive the UI on the html file, and a sql file describing the data you're displaying, then let's put all those files together in the same directory, instead of spreading them thin across separate folders
app/controllers/api/X/X.py
app/templates/X/X.html
app/js/views/X/X.js
app/css/X/X.css
Why not put them all under:
app/X/X.py
app/X/X.html
app/X/X.js
app/X/X.css
Related material:
Object-Oriented Programming is Garbage: 3800 SLOC example
https://www.youtube.com/watch?v=V6VP-2aIcSc https://www.youtube.com/watch?v=V6VP-2aIcSc
- thatswrong0 10y agoI think you're conflating clean architecture with a bunch of other things. All it is is a set of rules about how different layers of your app can depend on one another. It doesn't say too much, if anything, about code organization or design patterns like SRP or loose coupling or whatever. I say this because we follow it pretty religiously at my work and it never feels impractical nor does it really feel like it adds too much extra work to anything we do, nor does it feel like related code is far apart nor are our functions too small. In practice, following the clean architecture means, for example, that my business logic can't explicitly depend on some database related code. If we have business logic that needs do stuff in the database, it defines an interface of some methods that it expect the database repository to implement that it can use. It then has an implementation that satisfies that interface passed to it using dependency inversion (as pointed out in the article). So it really means that if some business logic related to, say, tweets needs to retrieve some from the database, it calls something like "tweetRepository.findAllTweets()", which returns a bunch of "Tweet" domain objects. It never sees database rows, it knows nothing about which database we use, etc. Our business logic is focused solely on dealing with the use case, and nothing to do with the nitty gritty of how we interact with the database or how we eventually return that data to a user that needs it. Which is great, because it means if we ever change how our database layer works, we only need to change our database repository code. We don't need to worry about changing business code if we can satisfy the same interface as before.