• Stars
    star
    102
  • Rank 335,584 (Top 7 %)
  • Language
  • License
    MIT License
  • Created almost 8 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Pragmatic & opinionated implementation of the Clean Architecture with a fixed base layout and simple-to-follow rules and conventions

A Clear Architecture

In my experience, development approaches like Domain-Driven Design and structural concepts as the Hexagonal Architecture or the Onion Architecture carry a lot of wisdom but don't necessarily provide practical guidance when it comes to starting off with a new project. After several unsatisfactory experiments, I felt a sort of relief when I first read about the Clean Architecture, which nicely aggregates some high-level concepts while simplifying things at the same time.

However, even the Clean Architecture doesn't provide a simple-to-follow recipe for layouting a project, naming classes, files and directories and deciding where to settle a particular functionality. So I trial-and-errored myself to the point where I had a rather concise, opinionated implementation of the Clean Architecture that prove useful in several projects — even and especially in combination with each other. Let me introduce you to the Clear Architecture.

Note: I won't go into too much detail about the various high-level concepts involved in the Clear Architecture but rather focus on their practical application. Follow the links to learn more about them.

Key objectives

  • Pragmatic, down-to-earth architecture with a fixed base layout and a concise set of rules and conventions
  • Reasonable balance of simplicity, usability, abstraction and high-level concepts
  • Independence of programming environment, frameworks and delivery mechanisms
  • Suitable for building libraries (to be used by other packages)

Three-tier architecture

In the Clear Architecture, source code is structured into 3 tiers that build on one another, most easily illustrated as concentric circles with the outermost one consisting of 3 complementary sectors.

Clear Architecture tiers

â‘  Domain tier

In a banking application, the domain layer holds the definitions of the bank account, the account holder, the currency etc. as well as their relationships with each other.

â‘¡ Application tier

  • Application specific business rules & services
  • Use cases orchestrating domain components
  • Translating between external requests from the â‘¢ client tier and domain logic (back and forth)

The application layer executes different types of bank transactions, provides a currency exchange service and so on.

â‘¢ Client tier (3 sectors)

  • Low-level implementation details and mechanisms
  • Public ports for external agencies (APIs, CLI, MVC components, etc.)
  • Infrastructural details (persistence strategy, database platform, frameworks, 3rd party library bindings, etc.)
  • Unit, functional and integration tests

The client layer implements the database operations, provides a web interface for online banking and a FinTS interface to be used by external applications.

Ports

The Ports sector is the public interface of your application. It accept requests from external agencies (e.g. the Web, the command-line, an embedding system etc.), communicates them to the infrastructure sector as well as the â‘¡ application layer and responds with the processed results. It represents your system to the outer world by taking the form of a web or native GUI, a CLI, a REST API or any other type of interface suitable for accessing your application. Components in this sector may include (but are not limited to):

Note: If your application provides a web interface or similar API, don't directly use the Ports directory (or any of its subdirectories) as document root for web server scripts. Instead, create a top-level public directory and put your bootstrap script files there. Search for a similar solution when providing a CLI (see below for PHP specific recommendations).

Infrastructure

The Infrastructure sector is not strictly private, but it holds the implementation details that are not necessarily of public interest. While the Ports interface should be stable in the long run, it's acceptable for infrastructural components to change with the requirements. Ideally, they can be swapped against equivalent mechanisms without affecting the overall system functionality. External agencies should avoid accessing the infrastructure layer directly, but there might be exceptions for efficiency's sake. Typically in this sector:

  • Third-party libraries and frameworks (they're always considered to be part of the infrastructure even if stored elsewhere and / or autoloaded)
  • Persistence mechanisms (e.g. database platforms)
  • Model / View / Presenter / Domain / Responder components of MVC / MVP / ADR architectures
  • Template engines and templates
  • Configuration data

Tests

The Tests sector holds all resources required for testing your application. In general, tests are nothing more than highly specialized clients of your application and must be granted full access to your â‘¢ client and â‘¡ application layers. Test resources may also be accessed by external agencies (e.g. by extension) and typically include:

  • Unit, integration, interface and / or acceptance test cases
  • Test fixtures

Directory layout

Base skeleton

`-- src
    `-- <Module> 
        |-- Application
        |-- Domain
        |-- Infrastructure
        |-- Ports
        `-- Tests
  • The top level directory src separates the actual source files from other package resources, e.g. documentation, dependency modules, web interface files etc.
  • <Module> must be replaced with a vendor-unique UpperCamelCase package name (e.g. MyApp).
  • The 3rd level is made up of five directories representing the main architectural tiers and sectors .

Application specifics

Inside the five main directories, your application may add additional structures as needed. However, to keep things consistent, I recommend sticking to these conventions:

  • Directory and file names are always to be written in UpperCamelCase. I prefer using singular expressions wherever possible (i.e. Factory instead of Factories).
  • If you have multiple similar components, that are mostly used by external agencies (e.g. on a lower architectural level or by an external package), keep them at a common central location. As an example, I typically use directories named Facade, Contract, Service or Factory for grouping classes with similar functionality.
  • Keep closely related components together. If you have, for instance, a class definition that implements an interface as described in The Dependency Inversion Principle, put them into the same directory instead of spreading them across the file system. This rule commonly outweighs the previous one — it might be a matter of taste in some situations though.
  • If a lower architectural layer "mirrors" and extends the structure of a higher one, e.g. by providing concrete implementations of a high-level interface, try to use common file and directory names accross levels. This will help keeping the cross-boundary relationships in mind.

Rules & Conventions

Clear Architecture tiers

The Dependency Rule

In the Clear Architecture, source code dependencies may only ever point to the same or an inward layer.

Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes functions, classes, variables or any other named software entity. (Clean Architecture, Robert C. Martin)

In other words, it's perfectly fine to reference same or higher-level components e.g. by

  • constructing class instances ("objects"),
  • implementing interfaces, using traits etc.,
  • calling functions and methods or
  • using classes, interfaces etc. for typing.

Adhering to the Dependency Rule makes your application testable and flexible in terms of implementation details (persistence strategy, database platform, provided client APIs etc.).

The Dependency Inversion Principle

In order to not violate the Dependency Rule, the Dependency Inversion Principle must be used whenever complex data needs to be passed across a boundary to an inward layer. Instead of expecting and directly referencing a low-level component (e.g. as a function parameter), the high-level layer provides and references an interface that must be implemented and inherited from by the caller. This way, the conventional dependency relationship is inverted and the high-level layer is decoupled from the low-level component.

Dependency inversion by using an interface / abstract service class

Decoupling & Dependency Injection

In general, avoid tight coupling between components and prefer abstractions / interfaces over concrete implementations, thus enabling polymorphism and making your application a lot better testable. You will always have to deal with object instantiation and globals at some point, but try to limit their occurrences to a minimum and manage them smartly. Some type of Dependency Injection mechanism might be of great help, but please consider the possible drawbacks as well. DI configuration should be part of the Infrastructure sector or of a general bootstrap process, stored somewhere outside the layer directories altogether.

Naming conventions

The following special components (and their files) must be named after their role:

  • Interfaces must use the Interface suffix (e.g. MyCustomInterface)
  • Traits must use the Trait suffix (e.g. MyCustomTrait)
  • Factories must use the Factory suffix (e.g. MyCustomFactory). Public factory method names must use the create prefix (e.g. createFromParams).

Considerations for PHP implementations

Note: I mostly use the Clear Architecture for projects written in PHP but it should be easy to adapt its principles to other environments as well. Please let me know if you succeed (or fail) in doing so.

  • Stick to commonly agreed coding style rules like PSR-1 and PSR-2. Consider using code quality checkers like Scrutinizer, Code Climate etc. Have a look at my Clear Architecture Yeoman Generator to speed up the installation and configuration of these tools.
  • Apply PSR-4 autoloading standards with <Module> being the base directory corresponding to the namespace prefix (e.g. Jkphl\MyApp). Follow the next point to get autoloading for free.
  • Use Composer as the principal dependency manager for your projects. By default, Composer installs package dependencies into the top-level vendor directory.
  • When providing a CLI, check out Composer's vendor binary features.
  • Use environment variables for configuration purposes (respectively the phpdotenv library or one of its equivalents).

Recommended readings

Legal

Copyright © 2017 Joschi Kuphal [email protected] / @jkphl. Licensed under the terms of the MIT license. Originally published at at https://jkphl.is/articles/clear-architecture-php

More Repositories

1

iconizr

A PHP command line tool for converting SVG images to a set of CSS icons (SVG & PNG, single icons and / or CSS sprites) with support for image optimization and Sass output. Created by Joschi Kuphal (@jkphl), licensed under the terms of the MIT license
CSS
483
star
2

micrometa

A meta parser for extracting micro information out of web documents, currently supporting Microformats 1+2, HTML Microdata, RDFa Lite 1.1, JSON-LD and Link Types, written in PHP
PHP
114
star
3

grunt-iconizr

SVG + PNG icon kit generator — Grunt plugin wrapping around node-iconizr that creates a CSS icon kit from a bunch of SVG files, serving them as SVG / PNG sprites or embedded data URIs along with suitable CSS / Sass / LESS / Stylus etc. stylesheet resources and a JavaScript loader for easy integration into your HTML documents
JavaScript
104
star
4

node-iconizr

SVG + PNG icon kit generator — A low-level Node.js module that creates a CSS icon kit from a bunch of SVG files, serving them as SVG / PNG sprites or embedded data URIs along with suitable CSS / Sass / LESS / Stylus etc. stylesheet resources and a JavaScript loader for easy integration into your HTML documents
JavaScript
82
star
5

squeezr

Another take on device-aware adaptive images and server side CSS3 media queries
PHP
79
star
6

generator-clearphp

Yeoman generator for scaffolding Composer based PHP projects, featuring a lot of integrations and advocating the use of The Clear Architecture
JavaScript
9
star
7

shortbread

Asynchronous, non-blocking loading pattern for CSS and JavaScript resources
JavaScript
7
star
8

indieweb-talk

IndieWeb talk slides about the history and basic building blocks as presented at several occasions starting 2016
HTML
4
star
9

sasswatch

A Gentoo Linux service / initscript for automatic background compilation of Sass resources
3
star
10

material.is

Material 2017 — Reykjavík, Iceland — A conference exploring the concept of the Web as a material
HTML
3
star
11

defr

A simple usage pattern and lightweight JavaScript library for deferred loading of external CSS and JavaScript resources with optional support for localStorage caching. Created by Joschi Kuphal (@jkphl), licensed under the terms of the MIT license.
JavaScript
3
star
12

gulp-iconizr

SVG + PNG icon kit generator — Grunt plugin wrapping around node-iconizr that creates a CSS icon kit from a bunch of SVG files, serving them as SVG / PNG sprites or embedded data URIs along with suitable CSS / Sass / LESS / Stylus etc. stylesheet resources and a JavaScript loader for easy integration into your HTML documents
JavaScript
3
star
13

responsive-images-css

HTML5-like responsive background images in CSS (sort of …)
PHP
2
star
14

edropub

An Editorially → Dropbox → Leanpub editing and publishing workflow
PHP
2
star
15

elevator

The Elevator pattern — type casting of user defined objects in PHP
PHP
1
star
16

html-formatter

A simple and opinionated HTML5 source code (fragment) formatter
PHP
1
star
17

dom-factory

Simple HTML5/XML DOM factory
PHP
1
star
18

gulp-concat-flatten

A Gulp plugin to recursively flatten directories and concatenate the files within
JavaScript
1
star
19

gulp-cache-bust-meta

A Gulp plugin for hash based (meta) cache busting
JavaScript
1
star
20

antibot

PHP
1
star
21

aevintyri

Ævintýri — a sophisticated event scheduling platform featuring organizers, venues, rooms, presenters, event series, events and sessions, featuring a JSON API compliant REST/CRUD interface
PHP
1
star