Minimize the framework

...

and allow yourself some DDD

"The Blue Book"

Clean Architecture Talk

Uncle Bob Martin

Text

... I'm a big fan

My Goals

  • Make the project tell its story from first glance
    • reflect this in the file structure as well
  • Decouple the domain logic from I/O
  • SOLID

Make the project tell its story from first glance

 

MVC Framework File Structure

  • Console
  • Controllers
    • Articles
    • Subscriptions
    • Comments
  • Helpers
    • DiscountCalculator
    • HeadingTrimmer
  • Models
    • Aricle
    • Subscription
    • Comment
  • Services
  • Views
  • generate-invoices.php

Decouple the domain logic from the I/O

Post::create()

public function create($request) { 
        // validate the data
        $this->validate($request, array(
                'title'         => 'required|max:255',
                'slug'          => 'required|alpha_dash|min:5|max:255|unique:posts,slug',
                'category_id'   => 'required|integer',
                'body'          => 'required'
            ));
        // store in the database
        $post = new Post;
        $post->title = $request->title;
        $post->slug = $request->slug;
        $post->category_id = $request->category_id;
        $post->body = Purifier::clean($request->body);
        if ($request->hasFile('featured_img')) {
          $image = $request->file('featured_img');
          $filename = time() . '.' . $image->getClientOriginalExtension();
          $location = public_path('images/' . $filename);
          Image::make($image)->resize(800, 400)->save($location);
          $post->image = $filename;
        }
        $post->save();
        $post->tags()->sync($request->tags, false);
        Session::flash('success', 'The blog post was successfully save!');
        return redirect()->route('posts.show', $post->id);
}

SOLID

We were going to:

  • Create a SPA
  • Make it live alongside WordPress
  • Use some data stored in WordPress posts in the SPA
  • Use the WordPress logged in user data

by

  • Creating a WP entry page for the SPA
  • Using WP's AJAX entry point
  • Loading the whole WP on every request
  • Masking it with adapter classes

Architecting

Started with the domain

  • Organized separate elements in packages
  • Introduced entry points - Use-Cases

Use Case

class SendInvoices {

    public function __construct(EntityManagerInterface $em, InvoiceSender $sender) {
        /* save dependecies */
    }

    public function execute(SendInvoicesParams $params) {
        /* stuff is happening here */

    }
}
class SendInvoicesParams {

    /* props, getters */

    private function __constructor() {}

    public static function fromArray(array $data) {
        /* build and validate */
    }
}

The UI

Not GUI...

HTTP UI

  1. Collect values from input variables into an array
  2. Create params for the use case needed
  3. Create the use case
  4. Execute the use case with params object
  5. Serialize return value
  6. Send to user

NO WAY TO REPEAT THIS EVERY TIME!!!

Generic configurable request handler

[
    'match' => [
        'method' => 'GET',
        'get.action' => 'list'
    ],
    'useCase' => [
        'className' => \Crawling\ListFoundProducts::class,
        'parametersMapping' => [
            'requestId' => 'get.crawlId',
            'search' => 'get.search',
            'page' => 'get.pageNumber',
            'sort' => 'get.sort'
        ],
        'serializer' => \UI\JsonSerializer::class
    ]
],
...

Then added some middleware to get the user id from WP

et voilà

The cake is not a lie

Photo by Daria Shevtsova from Pexels

...but what about the command line

[
    'match' => [
        'command' => 'invoices-send'
    ],
    'useCase' => [
        'className' => \Billing\SendInvoices::class,
        'parametersMapping' => [
            'testing' => '--testing::',
            'limit' => '--limit::'
        ],
        'serializer' => UI\ConsoleSerializer::class
    ]
],
...

It's absolutely the same!

A Recap

What we did was:

  • Create “entry” points in our domain in the form of command objects

  • Have the input to them in the form of value objects

  • Use middleware for non standard input

  • Map the relevant input from the environment to the “entry” points input

  • Execute the command

  • Serialize the return value and send it to the user

A Recap

What we achieved was:

  • A nicely layered architecture

  • A nice abstraction of the different inputs and outputs

  • Excellent separation of concerns between the domain logic and the infrastructure logic

  • Focus on the domain - the things that actually matter

  • SOLID code

  • First glance idea of the most important actions in out system

  • DDD

Action-Domain-Responder

Thank you

Copy of Minimize the framework and allow yourself some DDD

By Milko Kosturkov

Copy of Minimize the framework and allow yourself some DDD

By now we've all heard what Domain Driven Design is about. Some of us have actually tried it. When we did, we stumbled upon the fact that the frameworks we use do not help us a lot with shaping our code in a domain driven manner. Actually, they were in the way. DDD and the generic MVC frameworks we use are an oxymoron. Still, we don't want to loose all the sweet tools that help us deal with the HTTP and the Console. In this talk I'll tell you how we changed our view perspective on input/output/request/response, pushed the framework in the infrastructure corner and cleaned ourselves some space for sweet DDD.

  • 684