Evolutions of a Sandwich

Evolutions of a Sandwich

Unique evolutions of a Sandwich involve the system’s domain logic or its topology:

  • The domain-level services are independent enough to be easily added or removed.
  • In most cases they share technologies, allowing for splitting or merging of the services.
  • If the services are found to be strongly coupled, they can be merged into a monolithic layer, likely to be subdivided in a better way later on.
  • Alternatively, the subdomains can be further decoupled.

Add or remove a domain-level service#

Sandwich add remove Service

Patterns: Sandwich.

Goal: maintain effective development.

Prerequisite: a new subdomain emerges or an old one becomes obsolete.

Though the Sandwich architecture allows for subdomains to be pretty independent in their logic, you should update your system’s high-level structure whenever there are drastic changes in the domain knowledge or functional requirements. Creation or deletion of a component often means forming or disbanding a team (see Inverse Conway Maneuver).

Pros:

  • The system architecture is clear as it follows the domain knowledge.
  • The development teams remain narrowly specialized, thus effective.
  • Dead domain-level code is easily identified and removed.

Cons:

  • You may need to update your database schema.
  • A newly established team takes time to learn its area of responsibility, while disbanding an old team disrupts almost everyone on the project.

Split or merge domain-level services#

Sandwich split merge Services

Patterns: Sandwich.

Goal: maintain effective development.

Prerequisite: business requirements gradually diverge from your original vision.

Ideally, each service should be kept cohesive, while the services should be decoupled from each other. However, business likes to mess up your plans. If you ignore the results, your teams will be slowed down by mutual dependencies or become overburdened by the size of the components which they maintain. Therefore restructure both the system and teams once the divergence between the domain knowledge and system architecture starts to negatively impact development.

Pros:

  • The system architecture is kept clear as it follows the domain knowledge.
  • System components remain cohesive inside and decoupled from each other.
  • The development teams are narrowly specialized, thus effective.

Cons:

  • You will have to update the database schema and integration logic (use cases).
  • Splitting or merging teams disrupts them.

Merge all the domain-level services together#

Sandwich to Layers

Patterns: Layers.

Goal: improve the system performance and, possibly, development efficiency.

Prerequisite: the project is small but found to be strongly coupled.

Often the project grows in an unexpected manner. If you see that the domain-level services interact intensely, that likely means that you chose a wrong architecture. Revert to Layers to remove the artificial interfaces. You may also consider merging the domain-level teams if there are not too many people in them.

Pros:

  • Less indirection and boilerplate code.
  • Improved performance which can be further optimized.

Cons:

  • Now all the teams face higher system complexity.
  • The teams will share the codebase which means a high level of interdependency.

Subdivide both shared layers#

Sandwich to Layered Services

Patterns: Three-Layered Services (Layered Services).

Goal: fine-grained scalability, database performance optimization, limited fault tolerance.

Prerequisite: the subdomains are loosely coupled in both use cases and data.

It is natural to divide a Sandwich into Services, but only if your domain is not data-centric (built around a Shared Repository) and your use cases are not too complex (requiring an Orchestrator).

Pros:

  • Independent scaling and deployment of the services.
  • Database technologies can be chosen on a per service basis.
  • Simpler application and database components.
  • Limited fault tolerance – if one of the services fails, others may still respond to clients.

Cons:

  • Complex use cases are hard to implement or debug.
  • Poor latency for use cases that involve multiple subdomains.
  • Any coupling in the data impairs performance and increases costs.
  • Now you’ll have much more work for your DevOps.

CC BY Denys Poltorak. Editor: Lars Noodén. Download from Leanpub or GitHub. Made with odt2wiki and Hugo Book.