Published on December 15, 2024 • 12 min read
I've read Uncle Bob's Clean Architecture cover to cover. Twice. I've also read Domain Driven Design, Patterns of Enterprise Application Architecture, and about a dozen other books on software architecture. Here's the thing though - I only agree with about 80% of what they say.
Don't get me wrong, these are brilliant books with invaluable insights. But after 8 years of building software for real clients with real budgets and real deadlines, I've learned that architectural purity sometimes gets in the way of delivering value.
Early in my career, I was that developer who would insist on perfect layering, complete dependency inversion, and absolute separation of concerns - even for a simple CRUD application that would be used by 10 people. I'd spend weeks setting up the "perfect" architecture for what could have been a weekend project.
The wake-up call came when a client told me: "This is beautiful code, but we needed it working two weeks ago, and now we've missed our market opportunity."
That hurt. But it taught me something crucial: the best architecture is the one that serves the business need, not the one that looks prettiest in a diagram.
Clean Architecture isn't wrong - it's just not always right for every situation. Here's when I do reach for it:
When you're dealing with intricate business rules that change frequently, the separation of concerns becomes invaluable. I worked on a financial platform where regulatory requirements changed monthly. Having business logic isolated from infrastructure concerns meant we could adapt quickly without breaking existing functionality.
If your application needs to pull from REST APIs, GraphQL endpoints, databases, and file systems, the repository pattern and dependency inversion become your best friends. It's much easier to swap out a data source when it's properly abstracted.
When you're building something that will be maintained for years by different teams, the investment in clean boundaries pays dividends. Code is read 10 times more than it's written, so clarity matters.
If you know the client's business is growing and changing rapidly, building in flexibility from the start prevents costly rewrites later.
But let's be honest - not every project needs this level of architecture:
If you're building a basic inventory management system with standard create, read, update, delete operations, you might be over-engineering with full clean architecture. A simple MVC pattern with some service classes might be perfectly adequate.
When you're validating a business idea, speed to market often trumps architectural elegance. You can always refactor later if the idea proves viable.
For internal tools with well-defined, unlikely-to-change requirements, a simpler approach often makes more sense.
Here's how I actually approach architecture decisions:
I always begin by understanding:
Regardless of the overall architecture, I always favor component-based development. It promotes reusability and makes the codebase more maintainable without the overhead of full clean architecture.
I focus clean architecture principles on the areas most likely to change:
For most applications, a straightforward infrastructure setup is better than an over-abstracted one. I'd rather have a maintainable deployment pipeline than a perfectly decoupled infrastructure layer that nobody understands.
Let me give you a concrete example. I recently worked on an e-commerce platform for a client. Here's how I applied these principles:
What got full clean architecture treatment:
What stayed simple:
The result? We delivered on time, within budget, and the client could easily modify their pricing strategies and add new payment methods without touching core code.
Your architectural decisions should also consider your technology stack. With .NET (my preferred backend technology), the framework provides excellent support for dependency injection and layered architecture out of the box. This makes implementing clean architecture patterns much more straightforward than in some other frameworks.
Similarly, with Angular on the frontend, the built-in service and component architecture naturally supports clean separation of concerns.
One thing I've learned is that architecture becomes much more manageable when you automate the repetitive parts. I invest heavily in:
This automation lets me maintain architectural consistency without the manual overhead that kills productivity.
Good architecture serves the business, not the other way around. I've seen too many projects fail because developers were more concerned with following patterns perfectly than delivering value to users.
My advice? Learn the patterns, understand the principles, but always ask yourself: "Does this architectural decision help my client succeed?" If the answer is no, simplify.
Remember, you can always refactor towards better architecture as requirements become clearer and the application grows. It's much harder to recover from a missed market opportunity because you spent too long perfecting your dependency graph.
In my next article, I'll dive into my testing strategy - specifically why I think 90% code coverage is the sweet spot and how to write E2E tests that actually catch the bugs that matter.
Want to discuss architecture decisions for your project? Get in touch - I love talking about finding the right balance between engineering excellence and business value.