From framework coupled code to microservices through DDD modules. The presentation discussed the evolution from monolithic frameworks to microservices architecture through various stages:
1) Old days of framework coupled code with low autonomy, maintainability and learning curve.
2) Use of MVC frameworks improved isolation but code was still highly coupled.
3) Focus on testing drove adoption of SOLID principles at a micro scale.
4) Domain-Driven Design introduced modules per domain concept improving decoupling, semantics and testability.
5) Further decomposition into bounded contexts and microservices provided more autonomy for teams but introduced new accidental complexities around infrastructure and coordination.
9. <?php
/**
* The template for displaying all single posts
and attachments
*
* @package WordPress
* @subpackage Twenty_Sixteen
* @since Twenty Sixteen 1.0
*/
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main"
role="main">
<?php
// Start the loop.
while (have_posts()) : the_post();
single.php
10. role="main">
<?php
// Start the loop.
while (have_posts()) : the_post();
// Include the single post
content template.
get_template_part('template-
parts/content', 'single');
// If comments are open or we
have at least one comment, load up the comment
template.
if (comments_open() ||
get_comments_number()) {
comments_template();
}
if (is_singular('attachment')) {
single.php
12. ! Scripts
◕ index.php
◕ utils.php
◕ helpers.php
◕ include & require_once
The Concept
gOld days
13. ! Have to learn
◕ Mindset
◕ Toolset
◗ PHP – Namespaces y autoloader Composer
◗ PHP – Estilo de código, estándar PSR 2
◗ Generación automática de código con IntelliJ y PhpStorm
◕ Domain
◗ Incomprehensible Finder Kata Refactoring
! All is The Concept
The Concept
gOld days
16. <?php
class ctrl_frontend extends Controller {
function ctrl_frontend()
{
parent::Controller();
if (!isset($_SESSION)) {
session_start();
}
}
function index()
{
$this->load-
>model('sobre_nosotros','',TRUE);
THE Controller
21. ! Wins
◕ Controller per concept
◕ Isolate views & DB
◕ Active Record discovery
! New
◕ Singletons, singletons everywhere
◕ “Models” & other misconceptions
◕ DB structure defined by ORM
! Still
◕ Highly coupled code
◕ “Software Architecture is for UML people”
The Concept
MVC frameworks to the rescue
22. ! Internal doubts
◕ Por qué NO usar getters y setters | Tell don’t ask
◕ Por qué programar sin usar “else” – Cláusulas de guarda
◕ Varios returns en una función: ¿Mal o bien?
◕ Qué son los Code Smells y el Refactoring
◕ Constructores semánticos – Named constructors
The Concept
MVC frameworks to the rescue
23. class CourseController extends FOSRestController
{
public function getCourseAction(Request $request)
{
return $this->getDoctrine()
->getEntityManager()
->createQueryBuilder()
->select('c', 'v')
->from('AppBundleModelCourse', 'c')
->where('c.level', '>', $request-
>get('from_level', 0))
->getQuery()
->execute();
}
public function getCourseVideosAction($courseId)
{
return $this->getDoctrine()
Course controller
bit.ly/codelytv-course-ctrl-fw
24. class Course extends CourseBaseModel
{
public $title;
public $level;
/**
* @return mixed
*/
public function getTitle()
{
return $this->title;
}
/**
* @param mixed $title
*/
public function setTitle($title)
{
$this->title = $title;
Course model
bit.ly/codelytv-course-model-anemic
26. ! Wins
◕ If you don’t test, you’re going to suffer
◕ SOLID at a micro-design scale
! New
◕ Fragile test due to coupled code bases
◕ Test private methods or not?
! Still
◕ Controller per concept
◕ “Models”, singletons & laravel “facades”
The Concept
The holy grail of testing
27. class CourseControllerTest extends
PHPUnit_Framework_TestCase
{
public function testGetCoursesFilteredByLevel()
{
$fromLevel = 0;
$request = new Request(['from_level' =>
$fromLevel]);
$container =
Mockery::mock(ContainerInterface::class);
$doctrine =
Mockery::mock(Registry::class);
$entityManager =
Mockery::mock(EntityManager::class);
$queryBuilder =
Mockery::mock(QueryBuilder::class);
$query =
Course controller test
29. ! Testing help
◕ Cómo testear código acoplado: Costuras
◕ ¿Puedes ayudarte del #TDD para diseñar software? – Diseños
emergentes
◕ Cómo escuchar a tus test #NaveMisterioCodelyTV
◕ Por qué no usar static
! SOLID
◕ Principio de Responsabilidad Única SRP
◕ Principio de Segregación de Interfaces ISP
◕ Errores comunes al diseñar Interfaces
◕ Principio de Inversión de Dependencias DIP
The Concept
The holy grail of testing
31. ! Wins
◕ Decoupling business logic from framework
◕ Module per concept
◕ Semantics and clean code as something necessary
◕ Ease testing
◕ SOLID at a macro-design scale
◕ DB structure defined by domain
! New
◕ Unlearning process
◕ Deep research aptitude
◕ Theory misinterpretations
The Concept
Serious business
32. The growth stages of a programmer
1st stage 2nd stage 3rd stage
Knowledge Code complexity
Non scientific source :P : https://www.youtube.com/watch?v=2qYll837a_0
33.
final class VideoController extends Controller
{
private $bus;
public function __construct(CommandBus $bus)
{
$this->bus = $bus;
}
public function createAction(
string $id,
Request $request
) {
$command = new CreateVideoCommand(
$id,
$request->get('title'),
$request->get('url'),
$request->get('course_id')
Video controller
34. {
$this->bus = $bus;
}
public function createAction(
string $id,
Request $request
) {
$command = new CreateVideoCommand(
$id,
$request->get('title'),
$request->get('url'),
$request->get('course_id')
);
$this->bus->dispatch($command);
return new HttpCreatedResponse();
}
}
Video controller
bit.ly/codelytv-video-ctrl
35. final class CreateVideoCommandHandler implements Command
{
private $creator;
public function __construct(VideoCreator $creator)
{
$this->creator = $creator;
}
public function __invoke(CreateVideoCommand $command)
{
$id = new VideoId($command->id());
$title = new VideoTitle($command->title());
$url = new VideoUrl($command->url());
$courseId = new CourseId($command->courseId());
$this->creator->create(
$id,
$title,
$url,
$courseId
Create video command handler
bit.ly/codelytv-video-handler
36. final class VideoCreator
{
private $repository;
private $publisher;
public function __construct(
VideoRepository $repository,
DomainEventPublisher $publisher
) {
$this->repository = $repository;
$this->publisher = $publisher;
}
public function create(
VideoId $id,
VideoTitle $title,
VideoUrl $url,
CourseId $courseId
Video creator application service
38. final class Video extends AggregateRoot
{
private $id;
private $title;
private $url;
private $courseId;
public function __construct(
VideoId $id,
VideoTitle $title,
VideoUrl $url,
CourseId $courseId
)
{
$this->id = $id;
$this->title = $title;
Video domain model
39. $this->courseId = $courseId;
}
public static function create(
VideoId $id,
VideoTitle $title,
VideoUrl $url,
CourseId $courseId
)
{
$video =
new self($id, $title, $url, $courseId);
$video->record(
new VideoCreatedDomainEvent($id)
);
return $video;
}
Video domain model
bit.ly/codelytv-video-model
40. final class CreateVideoTest extends VideoModuleUnitTestCase
{
/** @var CreateVideoCommandHandler */
private $handler;
protected function setUp()
{
parent::setUp();
$creator = new VideoCreator(
$this->repository(),
$this->domainEventPublisher()
);
$this->handler =
new CreateVideoCommandHandler($creator);
}
/** @test */
Create video test
41. public function it_should_create_a_video()
{
$command = CreateVideoCommandStub::random();
$video = VideoStub::fromRawValues(
$command->id(),
$command->title(),
$command->url(),
$command->courseId()
);
$event = VideoCreatedDomainEventStub::fromRawValues(
$command->id(),
$command->title(),
$command->url(),
$command->courseId()
);
$this->shouldSaveVideo($video);
$this->shouldPublishDomainEvents([$event]);
$this->dispatch($command, $this->handler);
Create video test
bit.ly/codelytv-video-test
42. ! Tips / knowledge we’d like to have had
◕ Caso real de replanteamiento de diseño de Software
◕ Explicar Refactorings de forma didáctica – QWAN Cards
◕ Introducción Arquitectura Hexagonal – DDD
The Concept
Serious business
46. ! There’s no silver bullet approach. All depends on the context, and the
context will evolve.
! Conclusion: The Concept must be easy to promote with context evolutions
◕ From framework to modules:
◗ Decouple from outside infrastructure in order to isolate the domain
◗ Isolate use cases and work on cohesion
◕ From modules to Bounded Context:
◗ Decouple using buses to interact between them
◕ From whatever to Microservices:
◗ You don’t have to promote anything. This is not a change in terms
source code but in terms of infrastructure
The Concept
Recap
47. Tabla molona
Framework
coupled code
Modules
Bounded
Contexts
Microservices
Learning curve Low Medium High High++
Teams autonomy Low Medium+ High High++
Infrastructure
Shared (&
coupled)
Shared Individual
Individual &
distributed
Code
maintainability/
extensibility
Low— High High+ High++
Infrastructure
complexity
Low Medium Medium High++++
Communication
between them
Coupled Buses Buses Distributed buses
Deploy Shared Shared Shared Isolated
We’re not promoting microservices per se. Due to infrastructure and accidental complexity It could be the worst option actually.
49. Takeaways
! Unit Testing sucks (and it’s our fault) - José Armesto
! Vídeos sobre SOLID - CodelyTV
! Hexagonal Architecture - Chris Fidao
! Introducción Arquitectura Hexagonal – DDD - CodelyTV
! Domain-Driven Design in PHP - Carlos Buenosvinos, Christian Soronellas
and Keyvan Akbary
! A wave of command buses - Matthias Noback
! How to Avoid Building a Distributed Monolith - Felipe Dornelas
! PHP Barcelona Monthly Talk: DDD Applied & Symfony MPWAR Edition -
Sergi González & Eloi Poch
! Implementing Domain-Driven Design - Vaughn Vernon
! This talk repositories (to be published on github.com/CodelyTV)