Laravel 5.1 App Architecture

Backend Building Blocks

by Jason Judge / @JasonDJudge

Hosted Slides | Github Source
Press "S" for speaker notes

# Introducing * Commands * Jobs * Events * Sheduler * Queues What they are ~ When to use them ~ How they work Note: - Not intending to cover how to use them; see the L5.1 docs. - L5.1 rather than 5.2 as it is an LTS release. - Please interrupt to ask questions at any time
# Commands * Runs as CLI * `artisan` is the shell interface * Handles stdin, stdout, params, options * Can be interactive (prompts, menus, progress bar) * Closely linked to scheduler * L5.0 "commands" are now "jobs" Note: - artisan is a PHP shell script. - Can be piped, redirected etc like any shell command.
# Commands ## Where do they come from? * Many built-in * Custom commands under `App\Console\Commands` * Packages can bring their own * Run `artisan` so see the list of commands * Organised into groups (a scope) Note: - Scopes include route, migrate, make, etc. - Commands recognise and support applications that are more than just web requests; very powerful.
# Commands ## What goes in them? * Just the user interface (keep it lean) * Input (stdin and prompted) * Output * Arguments and Options * Final result code * Exception handler can be handy Note: - The command is just a shell for interacting with the user. - The main functionality should really be put somewhere else. - Letting exceptions fall through is a last resort; try to catch and present a user error.
# Commands ## Example - define it

							<?php namespace App\Console\Commands;

							class Hello extends Command
							{
								protected $signature = 'demo:hello';
								public function handle()
								{
									$this->info(sprintf(
										'Number of arguments: %d',
										count($this->argument())
									));
								}
							}
						
Note: - A very simple command to tell you how many parameters have been passed in. - Shows output (info message) and access to arguments. - As elsewhere, much left out of this for clarity; read the official docs.
# Commands ## Example - register it

							<?php namespace App\Console;

							class Kernel extends ConsoleKernel
							{
								// This array registers custom commands with Laravel.

								protected $commands = [
									...
									\App\Console\Commands\Hello::class,
								];
							}
						
Note: - Each command given as the full namespaced name.
# Commands ## Example - run it From the bash shell command line: $ ./artisan demo:hello 1 2 3 Number of params: 3 $ There are commands to help you create commands. Can also run a command from a controller. But why? Note: - This is run from a shell prompt here. - Keep commands for CLI interaction; there are better ways to run code through a controller.
# Queues ## Purpose * Certain objects can be run **asynchronously** * Commands * Jobs * Events A queueing system is where these objects queue up, waiting to be processed. Note: - A queue is generally FIFO and is dumb - it just holds some data.
# Queues ## Queue Management Services * Database * Beanstalkd * IronMQ * etc. Note: - Database requires no additional tools or system services, and is good for development.
# Queues ## The Laravel approach * Object serialised and thrown onto a queue * A queue worker deserialises and executes the object Laravel wraps queued tasks with into objects that *just run*; it does all the hard work for you A job can be set so it is queued by default when dispatched Note: - Object is JSON serialised. - Laravel tries to store IDs rather than objects, so it can fatch them later.
# Jobs * A job is a self-contained block of processing that has no user interaction. * A job is *usually* run asynchronously; we don't need to wait for it to finish. * Examples: send an email, run a report, archive old data. Note: - It can interact using event broadcasting, connecting it to the user's browser through a complex channel.
# Jobs Give a job class the `ShouldQueue` interface, and it will queue automatically when dispatched.

							<?php namespace App\Jobs;
							use Illuminate\Contracts\Queue\ShouldQueue;

							class CheckOrders extends Job implemkents ShouldQueue
							{
								public __construct($data_to_work_on) {
									$this->data_to_work_on = $data_to_work_on;
								}

								public function handle(AnyProvider $provider, ...)
								{
									// Do its stuff
								}
							}
						
Note: - The majority of jobs will be run like this; it is the main purpose of jobs.
# Jobs ## When would you dispatch a job? * To separate the trigger from the action * When something happens (an event) something else needs to be done (the job) * When you need to return control to the user quickly * Dispatch an asynchronous job Note: - A trigger may fire one job today, and several jobs tomorrow as requirements unfold.
# Events Events are a game of two halves: * Registered events *happen* * Listeners are *notified* Note: - Listeners don't actually "listen". They register themselves and are notified when they are needed.
# Events ## The event architecture * Ultimately each event has a name and an event class * The event class holds the data for the event listeners * One or more listeners can be registered against an event * The listener is handed the event (with its data) when despatched * A listener can be a class or a closure Note: - The L5.1 documentation shows events looking in at different abstract levels, and that can hide much of its depth. - Registered listeners are just in a big array, keyed on the event name.
# Events ## Where are events defined? Mostly: * System events * Eloquent events * Custom events Note: - Often custom events will be fired from eloquent events, after a simple check.
# Events ## Syntactic sugar Next three slides are all equivalent.

							// Register a closure to run when the Order model is updated.

							\App\Order::updated(function(\App\Order $order) {
								// Do stuff given order
							});
						
Note: - We register a closure as an "updated" event on the Eloquent model.
# Events ## Syntactic sugar

							// What the eloquent "updated" event registration actually does.

							Event::listen(
								'eloquent.updated: App\Order', // Event name
								function(\App\Order $order) {
									// Do stuff given order
								}
							);
						
Note: - This is the name of the event that the previous example constructs and registers for you. - This name is used here just to understand; it may change in Laravel core, so buyer beware.
# Events ## Syntactic sugar

							<?php namespace App\Providers;

							class EventServiceProvider extends ServiceProvider
							{
								protected $listen = [
									'eloquent.updated: App\Order' => [
										// Custom listener class to do stuff
										'App\Listener\OrderUpdated',
									],
									...
								];
							}
						
Note: - This $listen array is just a convenince, and nothing special.
# Events The provider listener array is for custom listener classes.

							<?php namespace App\Providers;

							class EventServiceProvider extends ServiceProvider
							{
								protected $listen = [
									'App\Events\OrderComplete' => [
										// Custom listener class to do stuff
										'App\Listener\OrderComplete',
									],
									...
								];
							}
						
Then fire the `App\Events\OrderComplete` event from the eloquent `updated` event when the status of the order goes to "COMPLETE". Note: - The name of a custom event is its namespaced name.
# Events If order has changed to COMPLETE then fire the custom event from this `updated` Eloquent event.

							\App\Order::updated(function(\App\Order $order) {
								// Order has changed status to COMPLETE.
								if (
									$order->getOriginal()['status'] != 'COMPLETE'
									&& $order->status == 'COMPLETE'
								) {
									Event::fire(new App\Events\OrderComplete($order));
								}
							});
						
An artisan command will create the event and listener classes from the array in the event provider. Note: - Register this during the application boot; you want to register events and listeners early.
# Task Scheduling Using `cron` you can schedule artisan commands to run at different times and intervals.

							# crontab
							# send outstanding emails every five minutes
							*/5 * * * * php artisan demo:send_emails
							# send payment reminders at 8am daily
							0 8 * * * php artisan demo:send_payment_reminders
							# etc
						
Or let laravel manage the schedules in code for you.

							# Call scheduler every minute
							* * * * * php artisan schedule:run >/dev/null 2>&1
						
Note: - The cron shedule parameters are great, but not friendly.
# Task Scheduling

							<?php namespace App\Console;

							class Kernel extends ConsoleKernel
							{
								protected function schedule(Schedule $schedule)
								{
									$schedule->command('demo:send_payment_reminders')
										->dailyAt('8:00');

									// etc for other jobs
								}
							}
						
The fluent scheduling options build into a cron schedule. Each minute Laravel then decides which of the console commands to run. Note: - There is a great range of fluent schedule options.
# Task Scheduling * All commands will run as if from the command line (system shell, artisan). * A command can be set to never run concurrently with itself (long running commands). * The schedule can be a custom function. * If a command misses its time slot, then it waits for next time. * Output can be directed to a log file. Note: - Often scheduled commands must not be allowed to run concurrently. - A custom schedule may run a command every hour, during each weekend after fulk Moon. cron could never do that.
# Summary * **Commands** run through artisan and can be scheduled or queued. * **Jobs** have no user interaction and can be run asynchronously (i.e. queued). * **Events** help separate concerns and keep each unit cleaner. * **Scheduled Tasks** schedule commands through code. * All these help to keep the app fast and responsive and easily extendable. Note: - None
Pulling all these components together ![Example system overview](Drawing1.png) Note: - This is a small slice from a real system that collects transactions from various remote systems and puts them into batches locally for further processing. - Fetching the data, and organising it into batches, are detactched processes, separated by the queue and triggered by an event. - A command fetches remote transactions and stores them in a model. - The model fires a created event which fires a custom New Transaction job. - The job is deserialised from the queue, given the transaction, which it can then process adding to a batch in this case (creating a new batch in the process if necessary).
# Thank you :-)