CQRS, ReactJS, Docker in a nutshell
-
Upload
andrea-giuliano -
Category
Engineering
-
view
693 -
download
2
Transcript of CQRS, ReactJS, Docker in a nutshell
CQRS, REACTJS, DOCKERIN A NUTSHELL
Andrea GiulianoClaudio D'Alicandro Simone Di Maulo
NEW AMAZING PROJECT
WE CAN WRITE IT FROM SCRATCH
BUT
immagine manager incazzato
WE NEED IT IN A VERY FEW TIME
AND
IT SHOULD BE
WTF!
WHERE DO WE START?
COMFORT ZONE
DOMAIN
▸ data come from and go to external entities
▸ users can configure to send a subset of data
▸ users send data based on their plan
send data from a source to a targetGOAL
THE DOMAIN
▸ unpredictable data structures
▸ ad hoc workflow for each vendor
▸ variable number of steps
▸ handle rate limits from different vendors
▸ handle different error cases from different vendors
▸ handle business-oriented limits (based on plans...)
▸ some tasks need to be done asynchronously
IDEA
BLACK BOX REASONING▸ identify the main entities involved
▸ define a common input and output
▸ find a way to let things talk
INPUT OUTPUT
FROM BLACK BOXES TO BOUNDED CONTEXTS
DEPENDENCIES INFRA BC
MODEL
APPLICATION
PRESENTATION
PROJECT DIRECTORY TREE
APP DIRECTORY TREE
INFRASTRUCTURE DIRECTORY TREE
INSIDE THE BOUNDED CONTEXT
BC DIRECTORY TREE
BC APPLICATION DIRECTORY TREE
BC MODEL DIRECTORY TREE
BC PRESENTATION DIRECTORY TREE
EVERYTHING'S AWESOME
▸ the framework is an implementation detail
▸ the directory structure is explicit
▸ the domain is isolated
WE DON'T WANT TO MESS THINGS UP
DON'T MESS UP THINGS
WHAT'S THE ISSUE HERE
▸ understandable?
▸ code can't be reused
▸ high coupling
▸ untestable
▸ too many responsibilities
▸ hard to find bugs
▸ not changes-prone
WHAT WE WANT
COMMAND QUERY RESPONSIBILITY SEGREGATION AKA CQRS
A SOLUTION
CQRS
▸ separe reads from writes
▸ commands perform actions
▸ queries return data
▸ heterogeneous data storages
▸ easy scaling
▸ deal with eventual consistency
WRITE STORAGE
QUERY
COMMAND
COMMAND
COMMAND BUS
COMMAND HANDLERDOMAIN
REPOSITORY
READ STORAGEREPOSITORY
EVENT BUS
EVENT SUBSCRIBER
IT'S ALL ABOUT BUSES
IT'S ALL ABOUT BUSES COMMUNICATION
INTERNAL COMMUNICATION
BC
EVENT
COMMAND
MESSAGE BUS
$ composer require simple-bus/message-bus
COMMANDS
COMMAND BUS
Represent the change that should be done in the domain
They are named with a verb in the imperative tense and may include the aggregate type, for example ScheduleATask.
COMMANDS
COMMAND BUS
CONTROLLER
$commandBus->handle( ScheduleATask::fromTaskId($taskId) );
HANDLER
public function handle(Command $command) { //do something with the $command }
EVENTS
BC 1 BC 2
EVENT BUS
An event represents something that took place in the domain. They are always named with a past-participle verb, such as TaskScheduled
EVENTS
BC 1 BC 2
EVENT BUS
subscribes_to: 'user-created'
subscribes_to: 'task-stopped'
subscribes_to: 'task-suspended'
EVENTS
BC 1 BC 2
EVENT BUS
$messageBus->handle( UserCreatedEvent::fromUser($user) );
subscribes_to: 'user-created'
subscribes_to: 'task-stopped'
subscribes_to: 'task-suspended'$messageBus->handle( TaskSuspendedEvent::fromTask($task) );
COMMUNICATION AMONG BCS
BC 1 BC 2
QUEUE
NETWORK
QUEUE
BC 1 BC 2
QUEUE
$producer->publish($message); $consumer->consume($message);
NETWORK
BC 1 BC 2
NETWORK
$httpClient->post('/tasks/schedule');
POST /tasks/schedule
SCENARIO: TRIGGER THE TASKS SCHEDULE EVERY 10 MINUTES
TIMER
SCENARIO: TRIGGER THE TASKS SCHEDULE EVERY 10 MINUTES
TIMER SCHEDULERPOST /tasks/schedule
SCENARIO: TRIGGER THE TASKS SCHEDULE EVERY 10 MINUTES
TIMER SCHEDULERPOST /tasks/schedule
DATA STORAGE
$taskRepository->getAll()
SCENARIO: TRIGGER THE TASKS SCHEDULE EVERY 10 MINUTES
TIMER SCHEDULERPOST /tasks/schedule
DATA STORAGE
$taskRepository->getAll()
TASK QUEUE
enqueue($taskId)
SCENARIO: TRIGGER THE TASKS SCHEDULE EVERY 10 MINUTES
TIMER SCHEDULERPOST /tasks/schedule
DATA STORAGE
$taskRepository->getAll()
TASK QUEUE
enqueue($taskId)
W1 W2 W3 W..N...
LET ME SEE WHAT YOU HAVE DONE
IT'S TIME TO SHOW DOWN
WHAT THE TEAM HAS DELIVERED
WHAT THE MANAGEMENT SEE
WHAT THE MANAGEMENT WANTS
LET'S START FROM THE TEMPLATE
TWIG
THE FRONTEND STUFF
THE FRONTEND STUFFORDER DEPENDENT
THE FRONTEND STUFF
GLOBAL SCOPE
<script> $('.btn').click(function(e){
e.stopPropagation(); // Do something cool! }); </script>
NEVER TRUST THE GLOBAL SCOPE
A STEP BACKWARD
WE ARE BACKEND DEVELOPERS
OUR COMFORT ZONE
OOP
ENCAPSULATION
MODULES
DEPENDENCY INJECTION
GOOD NEWS
ECMASCRIPT 6
DEFAULT VALUES
CLASSES
INHERITANCE
CREATE YOUR MODULES
IMPORT A MODULE
IMPORT ONLY WHAT YOU NEED
WHAT ABOUT THE UI?
var React = require('react'); var ReactDOM = require('react-dom');
ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('app') );
https://kangax.github.io/compat-table/es6/
ASSETIC CUSTOM FILTERS
ANOTHER STEP BACKWARD
REMEMBER THE BOUNDED CONTEXT
A LOT OF SMALL COMPONENTS
A LOT OF SMALL APPLICATIONS
BOUNDED CONTEXT FACEBOOK BOUNDED CONTEXT MAILCHIMPBOUNDED CONTEXT MAPPING
BOUNDED CONTEXT FACEBOOK BOUNDED CONTEXT MAILCHIMPBOUNDED CONTEXT MAPPING
Gulp Bundler
+
BOUNDED CONTEXT FACEBOOK BOUNDED CONTEXT MAILCHIMPBOUNDED CONTEXT MAPPING
DEVELOPMENT WORKFLOW
$ docker/gulp
docker-compose run --rm --entrypoint bash npm -c "gulp"
// gulpfile.js var gulp = require('gulp'); var hub = require('gulp-hub');
process .env .WEBPACK_CONFIG_FILE = path.join( __dirname, 'webpack.config.js' ) ;
hub(['src/**/gulpfile.js']);
BOUNDED CONTEXT FACEBOOK BOUNDED CONTEXT MAILCHIMPBOUNDED CONTEXT MAPPING
gulpfile.js gulpfile.js gulpfile.js
BOUNDED CONTEXT FACEBOOK
gulpfile.js
"## FacebookPresentationBundle.php $## Resources "## assets "## config "## public $## views
$ app/console assets:install
LET'S EXPOSE TO THE WEB
APPLICATION ENTRYPOINT
IT'S A BIG WORLD OUT THERE!
THE DEVELOPMENT ENVIRONMENT
▸ Easy to use so many technologies at no installation cost
▸ Prepare the scaffolding for a new developer is extremely simple
▸ Superior performances over previous systems
docker-compose.yml docker-compose.dev.yml
THE INFRASTRUCTURE
THE INFRASTRUCTURE
THE INFRASTRUCTURE
THE INFRASTRUCTURE
THE INFRASTRUCTURE
VS
THE INFRASTRUCTURE
VS
STAGE
▸ Automate image building
▸ Copy the same structure used in dev
STAGE
▸ Automate image building
▸ Copy the same structure used in dev
AUFS: VOLUMES MIGHT BE A LITTLE HARDER THAN IT SEEMS
SYMFONY PARAMETERS
incenteev/composer-parameter-handler
DOCKER CLOUD REPOSITORY CONFIGURATION
DATA ONLY CONTAINER
DATA ONLY CONTAINER
DATA ONLY CONTAINER
DATA ONLY CONTAINER
FIRST DEPLOY
AN ELEPHANT IN THE ROOM... WE NEED
▸ Automated deploy strategy
▸ The freedom to easily scale
SCALE
$ docker-compose scale \web=2 \worker=3
HARD TRUTH
fpm:
image: 'adespresso/hubespresso-staging:fpm-latest'
deployment_strategy: every_node
sequential_deployment: true
tags:
- fpm
- hubespresso
- production
volumes:
- /var/www/project
volumes_from:
- shared-fpm.hubespresso-production
SCALE CONTAINERS IS WORTHLESS IF YOU DO NOT SCALE NODES
HARD TRUTH
SCALE CONTAINERS IS WORTHLESS IF YOU DO NOT SCALE NODES
fpm:
image: 'adespresso/hubespresso-staging:fpm-latest'
deployment_strategy: every_node
sequential_deployment: true
tags:
- fpm
- hubespresso
- production
volumes:
- /var/www/project
volumes_from:
- shared-fpm.hubespresso-production
DATA ONLY CONTAINER IS A PAIN
DEPLOYMENT
▸ deploy the infrastructure is not straightforward
▸ multiple container in multiple nodes
▸ every container has its own lifecycle
▸ we are not assuring zero-downtime on deployment
THE SOLUTION: GREEN BLUE DEPLOYMENT
THE SOLUTION: GREEN BLUE DEPLOYMENT
THE SOLUTION: GREEN BLUE DEPLOYMENT
CONCLUSION
CQRSPHP7
DOCKERREACTJS
MONGODBWEBPACKGULP
LEAVE THE COMFORT ZONE
THANKS
QUESTIONS?