Architecture—the Lost Years

Uncle Bob's talk on Clean Architecture to the the Norfolk developers meetup group

Screaming Architecture

  • High-level directory structure

    • Why does the high-level directory structure tell me that it is a rails app instead of what the application does?
    • Why is the framework first and foremost instead of what the application does?
    • Why is the framework hiding what the application is supposed to do?
    • Why is the web in front of everything when it is an IO device?
      • The web changed nothing. The web is NOT the architecturally significant part of a system. Neither is rails etc
  • Tools, frameworks, are not architecture

  • What do architectures tell us?

    • What do blueprints tell us?
      • It's a library. It's a church. It's screaming at you.
    • Architecture is about intent
      • Frameworks are tools. Details
      • Details should be hidden not exposed

Application Logic

  • Books
    • Object-Oriented Software Engineering (a use case driven approach)
      • Not any difference between user stories and use case
  • Interactor objects
    • Contain application specific business rules
    • Always application specific
    • Based on use cases which are essentially user stories
    • Our application service classes are interactors (i.e., how to create an order)
    • Interactors usually talk with several entities
      • Order create interactor with a line item entity, order entity, and a customer entity
    • Interactors are just application specific services
    • Are separated by interfaces from IO (and entities?)
    • Shouldn't know the delivery mechanism (HTTP, console, etc.). Boundaries abstract this.
    • if create order, then it receives a create order request model
    • Very testable
      • You don't have to have anything up and running. The fundamental goal of writing tests is that you don't want the whole system running (especially when working with use cases)
      • Interactors and entities don't know anything about the web
    • Interactors don't necessarily need to be interfaced since they contain application-specific logic. The same most likely applies to entities. Interactors need to depend on abstractions, however. I've accomplished this by using DTOs.

The interactor derives from the input boundary (i.e., abstract class). The interactor orchestrates the dance of the entities. The output is handed to the output boundary from the interactor.

Business Logic

  • Business rules

    • There are business rules that are true across many applications (Application Independent)
      • Process of pricing an order. Adding up the prices of all of the elements
    • And then there are business rules that are specific to a single application (Application Dependent)
    • Process of creating an order is specific to the order entry system and therefore specific to the application
  • Entities (Or business objects)

    • Application independent business rules
    • Create the order, line item, etc.
  • Both of these should use services that abstract the framework away

Boundaries

  • Boundary objects (are essentially interfaces or abstract classes)
    • Sit between IO/delivery mechanism (and user) (23:43) and the interactor objects
    • Request model should be a plain old object and have primitives and no dependencies in it to ANY framework. It gets passed into the input boundary. Interactor receives the request model.
    • Result model is passed out through the boundaries to the end use

Boundaries are interfaces or abstract classes. Entities are concrete.

Every reference must go toward the interactor across the boundary. Presentation group and the business logic group should be different packages (DLL, Gem, package, etc.) and be deployed independently.

Independent deployment is the goal between GUI, business logic, and database.

MVC

  • MVC

    • MVC is not an architecture according to its inventor.
    • Very first named design pattern (probably) for small talk
    • Model: hides some tiny business rule
    • Controller: takes input from some device. Translates into method calls against the model. Model is not allowed to know what device is controlling the system.
    • View: is an observer relationship. Registers with the model and the model calls back to the view. Its job is to present the data within the model.
    • Input (controller), process (model), output (view)
    • Had MVC for a textbox, etc. back in the day. Didn't have MVC for a whole page (though that came later).
    • The problem with MVC for the web is that there are no solid boundaries.
      • View code gets in the models and model logic gets in the views.
  • MVP (Model View Presenter)

Model is not allowed to know where it gets its input from. The View registers with the model. Controller passes control to the View.

Model, View, Presenter (MVP 33:00)

The boundary between presenter and interactor is a hard architectural boundary (double black lines). All dependencies, point across the lines. Business logic doesn't know about MVP. The web is a plugin to the business rules.

Response model has data that is not presentable. Presenter converts those to strings.

Architecture

Application and business logic should be separate from the delivery method and the framework.

These things are simple but not easy. Team skill has a significant impact on what methods your team can use.

Databases became the significant decision because of Oracle and their push for DBAs. Rails made the mistake of making the web and the database the center of everything. Separate the details from the things that are general.

Defer decisions until they become unnecessary. The architect's job is to defer decisions as long as is possible. A good architecture allows significant decisions to be postponed. The role of an architect is not to make decisions but to defer decisions. This allows the program to be built in the absence of decision. A good architect maximizes the number of decisions not made. The plugin model enables this.

You don't need to know what web framework you're going to use. Get all of the business rules working first, get all of the flows working first, and then use a framework.

Entities are not instances of DB tables.

Frameworks

Framework authors are out to screw you. You make a tremendous commitment to the framework, but they do not commit to you. Frameworks come at a cost. They come with benefits but also with costs. Keep them at arm's length. Don't follow their examples.

Don't couple your code strongly to it. You should be free to break away from the framework.

Plugins (36:00)

  • Resharper example

Plugins are vulnerable to the things they plug into. The things that they plug into are immune at compile time. Things that need to be immune from other things should be plugins. The UI should be separate from business rules.

You can test the elements of the plugin architecture independently.

Discipline

  • As an industry, we lack discipline.
    • Half of us don't write tests

The database is a detail. Programmers are abstracters, detail managers.

Architects are not detached from the code. They are merely the best programmers.

The Database

SQL is horrible. The database access layer is textual which is crazy and vulnerable to injection.

The reason for the DB is disappearing. The why for NoSQL.

Don't bind yourself to the DB. You don't need disks anymore.

Entity Gateways

Double line boundary for the entity gateway. Has a method on it for every operation you want to perform on the DB. Every sort of query you want to do is behind a method.

  • getOrdersSince(someDate)

The database is a plugin to the business rules. You can separate the business rules from the DB. This fixes the slow test suite issue. Get the test process to be the QA process.

Dependency Injection (1:09)

You probably don't want to inject everything. Inject factories and let the business rules use those factories. Don't inject more than a dozen or two dozen items.

Make injections into a plugin. You don't want to use cases to know about injection framework.