Software Architecture — Principles, Practices & Styles

What is bad architecture and how to recognize it?

For the sake of velocity of development, developers often put in bad code, which ultimately leads to what we traditionally call as the spaghetti code. This leads to functional paralysis at some point in time, where the cost of building afresh is cheaper than fixing the existing code. Some of the characteristics are

  1. Unnecessarily Complex — Ironically it is easy to write complex code, anyone can do it, but it is hard to write simple code.
  2. Rigid/Brittle — Since it is unnecessarily complex, it is not easy to understand and therefore making it non-maintainable, easy to break for even a small code change.
  3. Untestable — Such code will be tightly coupled, will typically not follow the single responsibility principle, will be difficult to test.
  4. Unmaintainable — Brittle code with less test coverage evolves to becomes a maintenance nightmare

What is good architecture and what properties do they exhibit?

  1. Simple — Easy to understand.
  2. Modularity/Layering/Clarity — This is important so that one layer is able to change independently of the others with minimum coupling between the layers
  3. Flexible/Extendable— Can be easily adapted to new evolving requirements
  4. Testable/Maintainable — Easy to test, add automated tests, and encourage the culture of TDD and therefore maintainable

Why bother about architecture, principles, practices?

Cost reduction — Though initially, the velocity of development will be less, but eventually, the overall cost of building and maintenance will be less

What tools, methodologies, and techniques can we follow?

  1. Lean principles — Build the right thing. Build what is necessary.
  2. Agile methodology— Build the right way. Build software in a way that is agile, adaptable, fast to respond to changing market requirements
  3. Test-driven development practice & Automated Tests — Test drive implementation ensuring testable software design. This supports the Shift Left methodology, “Test early, Test often” resulting in maintainable code as it eliminates the fear of breaking existing functionality un-intentionally.

What architecture styles are followed?

Typically there is never one size fits all. Design decisions for a problem statement depend on the context and every design has its trade-off. Below are some of the most commonly used architecture styles that typically come together. What combination fits best for your application is best known to you.

  1. Domain Centric Architecture
  2. Application Centric Architecture
  3. Screaming Architecture
  4. Microservices architecture
  5. Event-Driven Architecture — EDA
  6. Command Query Responsibility Segregation — CQRS

Domain Centric Architecture

The domain is at the center of the model and everything else is built around it, the application layer, the presentation layer, the persistence layer, notification service, web services, etc. i.e. Domain is essential and everything else is just an implementation detail that is replaceable.

Original source:
Original Source:
  1. This allows for Domain-Driven Design (DDD) thinking. The focus is on a domain, users, and use-cases.
  2. Reduced coupling between domain (stable and changes less) and the implementation detail (which changes faster like presentation layer, database)
  1. The initial cost is higher as more time/thought/discussion has to be put into separate models needed for the domain versus application layer rather than all models just put together.
  2. Developers tend to avoid it, as it requires more thought. They stick to the old 3 layered Database Centric Architecture

Application Centric (Layered Architecture)

Once the domain boundary is defined, the application layer comes next. The application layer is made robust by applying the below SOLID principles.

Original source:

Functional Organization of code (Screaming Architecture)

The architecture should scream the intent of the system — Uncle Bob

Original Source:
Original source:

Microservices Architecture

In the old days, we had a single unified domain model to represent the customer or the product in the sales context and the support context. For example, the support-contact and the sales-customer were modeled as a single Customer model. As the solution space becomes bigger to have more and more domains, we add more attributes, properties, and validation rules that apply to only one domain but which are not true for the other domain leading to the unnecessary overhead of complexity of such unified code.
  1. The initial cost is higher
  2. DevOps automation is necessary as automated deployment is now a need.
  3. Extra time and cost is incurred to deal with such distributed computing in terms of latency, load balancing, logging, monitoring, dealing with eventual consistency, etc

Event-Driven Architecture (EDA)

Microservices can now communicate with each other using a request/response mechanism like JSON over REST calls OR using an event-driven architecture with a message-broker. Modern architecture prefers EDA as it allows the services to more responsive, reduced latency, robust, fault-tolerant, guaranteed service, and allows to scale better.

CQRS pattern-Command Query Responsibility Segregation

The advent of microservice and EDA also has given birth to the CQRS pattern. The command is something that modifies the state of the underlying object & the query does not modify the object but just returns the requested subset of objects.

  1. You can improve your read scalability without impacting the write. For example, by adding more secondary nodes in MongoDB to serve the read requirements, you selectively scale the read capability.
  2. The command has to send the updates to the database. You may choose to have a caching layer to provide for faster reads.
  3. Sometimes the object may belong to another microservice and querying another microservice every time may be costly, so you can use the caching layer for your query needs. Data is duplicated but as long as it is maintained, and is not changing very fast, you are able to reduce the latency to a good extent. This sometimes offers resilience too. Even if the other microservice is not available your microservice can continue to work normally. ex. Caching the product catalog in the Order-microservice.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sarada Sastri

Sarada Sastri


Java Architect | MongoDB | Oracle DB| Application Performance Tuning | Design Thinking |