Domain Driven Design
[Framework] is not your Application
Vocabulary
- An application is a module or group of modules which implement a specific piece of functionality.
- A module is a collection of one or more functions which can be exported and utilized as a group. Depending on the language context, a module may or may not be stateless; some languages might refer to a module as a class.
- A monolith is a singular repository or application which implements behaviors across a wide range of domains.
- An interface is one or more applications which mediates a communication protocol for another application.
The Problem
Modern web frameworks invite you to install the framework first and then start developing your application. They encourage you to think about your app in the context of the framework rather than the context of the objectives you are trying to meet. As a result, you may end up smearing your concerns and logic around your filesystem in accordance with the framework's requirements. You also inherit a lot of boilerplate which isn't necessarily relevant to your objectives.
Who's context is it, Anyway?
Dave Thomas and Robert Martin make very compelling arguments that frameworks should be thought of as plugins to your application or interfaces to the Internet. It isn't a Laravel app. It isn't a Phoenix app or a Rails app. It's a: blogging app, banking app, search engine, content aggregator, etc. Your code right down to the filesystem, should express your intent. Starting with a framework is an invitation to write a monolith because frameworks are, by their very nature, monolithic: they pre-package as many domains as possible to ease your integration (with the web, in this case).
…but it can certainly muddy the waters.Code you don't invoke won't impact performance.
Don't use patterns, discover them
Instead of smearing your code around a frameworks' prepared file-structure, why not just put all of your code as a single, gigantic block of procedural code in a single place? And then, as it gets unwieldy, or patterns/consistencies begin to appear, break that code up into functions and classes?
The magic of Interfaces
If you have a cross-cutting concern, eg: if you need to rely on some domain outside the one you're working on, then why not use an interface? If you code to an interface, you can then use dependency-injection to fulfill that interfaces' requirements from your framework.
Instead of
composer create-project laravel/laravel my-app…
- Create a brand new, empty composer project.
- Create a src/ directory at the top-level which is auto-loaded with psr-4 into some meaningful namespace.
- Create an index.php file in the project root where I write all the functionality of my app with as few dependencies as possible.
- As chunks of code begin to have meaning, wrap them up into classes and move those classes into the src/ directory, replacing the code in index.php with those classes and methods.
- Create a README which either references that index.php file, or copy-pastes meaningful chunks from it to demonstrate how the application operates.
Finally, create a new laravel app and require my app as a dependency of that laravel app. I then wire up whatever models, views, controllers, commands, jobs, etc. which alias to my app as much as possible. Using the above method, each domain can be its own little app and, if you want a monolith then you can compose them all into a single Laravel instance. Or, if you'd prefer a microservices approach, you could wrap each of them up into its own laravel app and deploy them separately.