Layered Web Application Architecture
PHP developers love the web-interpretation of MVC that was popularized by various frameworks a decade ago. Yet, MVC is itself a rather large-grained approach to resolving web architecture problems. Domain Driven Design provides a more detailed approach, but much of it's design is again mapping the design of a threaded deaktop application to the web.
On a high level, we have monolithic web server-side rendering web applications, the more contemporary models of splitting the application between a rich client-side rendered front-end and back-end REST API, and then internal concerns with the model -- whether to handle the entire process in the same thread or introduce [[worker driven]] approaches to resolving tasks.
Working towards a more fine-grained approach to the layers of a web application, and borrowing heavily from the DDD school, I arrive athe following example of how I see a web application architected.
Path of a Request
Passing through the application a Request flows through each of these layers:
- Client makes an HTTP request to the server
- Server routes the HTTP request to a Controller. Authentication and authorization is handled at this juncture.
- Controller uses a [[Validator]] to validate and normalize the request
- Controller delegates to an [[Application Service]] to perform the Command and marshalls any resources the service requires.
- Application Service uses [[Repositories]] to retrieve [[Models]], and if required, mutates those models
- The result of the Application Service (wether a success or failure) is returned to the Controller
- The Controller uses Repositories to Persist any Models returned and passes their values to a [[View]] (can be of HTML or JSON media types), renders the View and returns it to the Client.
A light-weight framework like Slim (my preference), can be incorporated to provide routing and basic functionality for ensuring correct handling of HTTP headers in the request/response side. The Validator, Application Service, Repository, Models, and View can all be implemented as very simple interfaces within the application.
The introduction of the Application Service is optional. In very simple CRUD applications, the additional indirection is uneccessary. The controller should use the Repositories to directly fetch, set the properties on the model (if needed), persist (if needed), and render the view. The Application Service should be introduced when we (1) have multiple views, e.g. support both a JSON and HTML rendering or this functionality is available through multiple routes in the application or (2) the business logic has expanded beyond simply serializing the contents of a request or fetching a single aggregate from the system.
This works both for client-side rendered applications, but also for REST APIs.