Software Architecture - herougo/SoftwareEngineerKnowledgeRepository GitHub Wiki

Clean Architecture (Uncle Bob)

An architecture for software where you have the following layers.

  1. Domain (enterprise-level business rules): includes entities, enterprise-level exceptions, enums, value objects, events, etc
  2. Application (application-specific business rules): use cases (commands, queries, event handlers)
  3. Interface Adapters: includes controllers, presenters, gateways
  4. Frameworks and Drivers: includes external interfaces, UI, DB, Web, devices

The point is that we have a single direction of dependency for the layers (i.e. 4 depends on 3, which depends on 2, which depends on 1). 2 never depends on something from 3 for example.

To make use of a database in the application layer for example, you have interfaces which are implemented in layer #4. You pass those objects as arguments to functions, constructors, etc. This way, the DB is mockable.

Advantages of clean architecture:

  • highly testable
  • framework independent
  • DB independent (database should be a detail/plugin)
  • UI independent

Disadvantages ( https://www.geeksforgeeks.org/what-is-clean-architecture-in-android/ )

  • steep learning curve (i.e. learning how all levels interact)
  • many extra classes

The video mentioned above refers to an "interactor" object. What is an interactor exactly? I'm not sure, but it seems like a black box which receives input, processes entities using business rules, then returns an output.

Model-View-Presenter Pattern: have a presenter module between the model and view, which transforms the data into something usable by the view (e.g. converting objects to strings).

Command - a directive to a computer program to perform a specific task

Query - request for information

Clean Architecture Template by Jason Taylor

Instead of having 4 layers, we have 3 layers, with the outermost layer being split into 2 parts (infrastructure and UI).

  • Domain
  • Application
  • Infrastructure / UI

Project structure (for to do items as an example)

  • Domain: folders for entities, enums, events, exceptions, and value objects
  • Application:
    • folder structure: /<feature_type>/...
      • entity can be TodoItems or TodoLists
      • feature_type can be query, command, or event handler
    • folders in common folder: behaviours, exceptions, interfaces, mappings, models, security
  • infrastructure
    • contains persistence, date service, etc
  • WebUI
    • contains angular code, controllers, etc

Advantages

  • (advantages of the clean architecture)

Disadvantages

  • Many files: one file for the validator, one file for the command itself, etc

Questions:

  • What are the behaviours, interfaces, mappings, and models folders about?
  • How would I build this from scratch (how is the code set up from a high level)?

My Thoughts

Cons

  • Can make coding more difficult (especially in python). Let's say you want to use Datadog to do some logging. Traditionally, you would create a datadog_logging.py file and write your logger wrapper, create a global variable as an instance of that wrapper class, then import that global variable whenever you need to log something. However, this goes against the clean architecture paradigm (I think) because you're depending on something from the outermost layer. Furthermore, if you try dependency injection, well now you need to pass around this logger everywhere in the arguments.
    • I think this is mainly done for Java, and in python, which has much more flexibility, this makes coding more difficult.

Derek Comartin (CodeOpinion) Modified Architecture

Basic idea:

  • suppose we have the layers as horizontal
  • consider organizing the code by features and taking vertical slices

His project structure

  • Application
    • Common
    • Entities
    • Features
      • TodoItems
        • CreateTodoItem.cs (contains controller, command, validator, event, etc in one file)
        • ...
      • TodoLists
    • Infrastructure
    • ValueObjects
  • Web UI

Related Article for making this work in C#: https://scottsauber.com/2016/04/25/feature-folder-structure-in-asp-net-core/

Model-View-Controller

wesleywerner's Implementation

This implementation of a python game using MVC uses an EventManager to manage the events.

https://github.com/wesleywerner/mvc-game-design

ev = EventManager()
model = GameModel(ev)                  # ev register listener
controller = GameController(ev, model) # ev register listener
view = GameView(ev, model)             # ev register listener
model.run()

Thoughts

  • Con: ev registers all as listeners
  • I think the config setup of event management should live inside the controller

My Implementation

Status: Work In Progress

  • problem: How does MVCEventManager work?
controller = GameController()
controller.run()

class EventReceiver:
  def notify(self, event):
    pass

class GameController(EventReceiver):
  def __init__(self):
    self._event_manager = MVCEventManager()
    self._model = GameModel(self._event_manager)
    self._view = GameView(self._event_manager)
    self._event_manager.register_mvc(model, view, self)
    # controller -> model -> view

  def register_click(self):
    pass

Modular Monolith

Article: Milan Jovanovic

source: https://www.milanjovanovic.tech/blog/what-is-a-modular-monolith

A modular monolith is an architectural pattern that structures the application into independent modules or components with well-defined boundaries. The modules are split based on logical boundaries, grouping together related functionalities. This approach significantly improves the cohesion of the system.

The modules are loosely coupled, which further promotes modularity and separation of concerns. Modules communicate through a public API.

Modular Monolith Diagram

Example folder structure

  • components
    • payments
      • public_api.py?, controllers, validators, etc
    • reviews
    • users
    • bookings
    • notifications

Modular monoliths have many benefits. So, I want to highlight a few that I consider important:

  • Simplified deployment - Unlike microservices, which require complex deployment strategies, a modular monolith can be deployed as a single unit.
  • Improved performance - Communication between modules occurs in-process. This means that there's no network latency or data serialization/deserialization overhead.
  • Enhanced development velocity - There's a single codebase to manage, simplifying debugging and the overall development experience.
  • Easier transaction management - Managing transactions in a distributed system is very challenging. Modular monoliths simplify this since modules can share the same database.
  • Lower operational complexity - Modular monoliths reduce the operational overhead that comes with managing and deploying a distributed microservices system.
  • Easier transition to Microservices - A well-structured modular monolith offers a clear path to a microservices architecture. You can gradually extract modules into separate services when the need arises.

Article: by Shopify

Source: https://shopify.engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity

Folder Structure Before

  • app
    • assets
    • channels
    • controllers
    • helpers
    • jobs
    • mailers
    • models
    • views
  • bin
  • config
  • db
  • lib
  • test

Folder Structure After

  • components
    • apps
    • billing
    • checkouts
    • taxes
      • app
        • assets
        • channels
        • controllers
        • helpers
        • jobs
        • mailers
        • models
        • views
      • config
      • lib
      • test
  • bin
  • config
  • db
  • lib
  • test

Monolith

Monolith disadvantages

  • Simple changes can result to a cascade of test failures.
  • Steep learning curves
  • Scalability (can't scale individual components)
  • Deployment (can't deploy frequently)
⚠️ **GitHub.com Fallback** ⚠️