Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shrek, Onions and Architecture - PHPDay 2024

Shrek, Onions and Architecture - PHPDay 2024

Have you ever encountered a codebase that's ogreish and has so many layers of complexity that it makes you cry? It might actually be an onion! In this humorous talk we'll investigate the structure of large monolithic applications and how we can peel back layers to reveal greater understanding. Using the principles of hexagonal architecture and the science of onion growth, we can make monoliths great again.

Katy Ereira

May 16, 2024
Tweet

More Decks by Katy Ereira

Other Decks in Programming

Transcript

  1. Katy Ereira - #PHPDay24 - @[email protected] It’s only logical…. •

    There are so many layers. • It smells (or at least, engineers act like it does). • Biting into it without preparation leaves a bad taste in your mouth. • It makes you cry. Introducing: the monionlith
  2. Katy Ereira - #PHPDay24 - @[email protected] Agenda 01 Growing onions

    Learn the secret to a monionlith’s robustness, from the ground up. 02 Cooking onions Making monionliths palatable - top tips for coding without tears. 00 Peeling onions Demystifying a mononiolith’s anatomy and its architectural layers.
  3. Katy Ereira - #PHPDay24 - @[email protected] The layers of an

    onion can be separated one at a time, from the outside in. 00 Hexagonal architecture and dependency inversion
  4. Katy Ereira - #PHPDay24 - @[email protected] 1 An onion. Skin:

    application layer Flesh: business logic Base: core domain model Roots: infrastructure Leaves: presentation 2 3 4 5 1 2 3 4 5
  5. Katy Ereira - #PHPDay24 - @[email protected] Flowers and leaves •

    Concerned with visibility and accessibility from the outside. • Provides entry points for interacting with the internals. • Is attached to and aware of all inner layers. presentation 🧅 Fun onion fact: onions usually flower in the second year as they move between growth and reproductive phases.
  6. Katy Ereira - #PHPDay24 - @[email protected] ➔ HTTP routes ➔

    REST API entry points ➔ GraphQL schemas ➔ HTML templates / views Flowers and leaves presentation
  7. Katy Ereira - #PHPDay24 - @[email protected] Skin • Facilitates safe

    interactions with the system. • It’s thin; does not provide any business logic. • Receives and directs requests, and returns responses. application layer 🧅 Fun onion fact: dried leaves which form the skin of an onion are also known as the ‘tunic’, and it protects the onion from pests and disease.
  8. Katy Ereira - #PHPDay24 - @[email protected] Fleshy layers • Form

    the bulk of the system; provides business logic. • Are distinct and separate from each other. • Are implementations of core abstractions. business logic 🧅 Fun onion fact: the fleshy layers that form the bulk of an onion are a type of succulent leaf that stores water and nutrients.
  9. Katy Ereira - #PHPDay24 - @[email protected] ➔ Domain services ➔

    Helpers ➔ Processes / algorithms Fleshy layers business logic
  10. Katy Ereira - #PHPDay24 - @[email protected] Base • Contains the

    building blocks for all future growth. • Encapsulates core concepts. • Has no substance in and of itself. core domain model 🧅 Fun onion fact: the base of an onion is actually an extremely short and wide stem, and it contains stem cells.
  11. Katy Ereira - #PHPDay24 - @[email protected] Roots • Communicates outwards

    from the system. • Interacts with external infrastructure. • Is well hidden from the public. • Enriches the system internals. infrastructure 🧅 Fun onion fact: onion roots grow from the underside of the base stem in contrast to leaves and flowers which grow from the top.
  12. Katy Ereira - #PHPDay24 - @[email protected] ➔ SDKs ➔ Databases

    & ORMs ➔ Frameworks ➔ Adaptors Roots infrastructure
  13. Katy Ereira - #PHPDay24 - @[email protected] Onions grow from the

    basal stem whose cells contain the information for growth. 01 Domain driven design.
  14. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Surveyance Talk to domain experts and business stakeholders to gain understanding and become fluent in Ubiquitous Language . Write User Stories to encapsulate the problems your system aims to solve. Define API Schemas . Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  15. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Encapsulation A seed contains cells that have instructions required for growth; but they themselves are small. Define Entities and Value Objects , making sure to speak Ubiquitous Language. This is your Core Domain Model . Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  16. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Emergence Now we implement business logic. This defines how domain objects transact. Onions grow in layers. Each layer is an independent Bounded Context . Bounded contexts should only reference the Core Domain Model. Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  17. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Integration External infrastructure supports growth. Our domain base provides interface Ports that can be implemented as Adaptors for specific infrastructure. We can add adaptors for ORMs, messaging and event services, and logging. Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  18. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Application It’s time to put everything together: user stories become Use Cases via controllers. Adaptors are plugged into ports by using Service Providers . Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  19. Katy Ereira - #PHPDay24 - @[email protected] Growing onions A step

    by step guide Bloom! In this final stage, add routes and register API schema definitions. Implement graphical user interfaces, views and templates - this is the Frontend . Groundwork Plant a seed Germinate Set root Develop a skin Bloom and harvest!
  20. Katy Ereira - #PHPDay24 - @[email protected] One does not simply

    bite into an onion. One must master the elements of good cooking. 02 Coding without tears.
  21. Katy Ereira - #PHPDay24 - @[email protected] 🧂 Salt • Add

    comments - but only when they improve clarity. • When naming, use Ubiquitous Language. • Reference explicit types wherever possible. • Don’t go overboard. add clarity 󰱜 Samin says, “The three basic decisions involving salt are: When? How much? In what form?”
  22. Katy Ereira - #PHPDay24 - @[email protected] /** * @throws NotFound

    */ public function getBar( Foo $foo, ): Bar; Don’t do that Do this /** * @param Foo $foo * @return Bar */ public function getBar( Foo $foo, ): Bar;
  23. Katy Ereira - #PHPDay24 - @[email protected] interface Checkout { public

    function submit( Order $order, ); } interface Order { /** * @return Item[] */ public function getItems(): array; public function getCustomer(): Customer; } Don’t do that Do this // Processes orders interface Handler { /** * Submit an order * @param Order|object|array $arg */ public function process( mixed $arg, ); } interface Order { // Contains customer and items. public function props(): object; }
  24. Katy Ereira - #PHPDay24 - @[email protected] public function useCase(): void;

    /** * @throws UpdateFailed */ private function updateFoo(Foo $foo): Foo; /** * @throws EventFailed */ private function emitEvent(): void; Don’t do that Do this /** * This actually does 2 things: * first it updates Foo * then it maybe emits a Bar event. * * Returns the Foo if successful. * Else it will return an error string. */ public function doThing(): mixed;
  25. Katy Ereira - #PHPDay24 - @[email protected] 🧈 Fat • Don’t

    define unnecessary get and set methods. • Consider whether attributes are optional or required. • Beware of invalid state. • If something is mutable; think about why. keep it lean 󰱜 Samin says, “fat plays three distinct roles in the kitchen: as a main ingredient, as a cooking medium, and … as seasoning.”
  26. Katy Ereira - #PHPDay24 - @[email protected] class User { public

    function __construct( private readonly UUID $id, private readonly string $name, ); public function getId(): UUID; public function getName(): string; } Don’t do that Do this class User { public function getId(): UUID; public function setId(UUID $id); public function getName(): string; public function setName(string $name); }
  27. Katy Ereira - #PHPDay24 - @[email protected] interface Phone {} interface

    Address {} interface ContactDetails { public function getAddress(): Address; public function setAddress( Address $address, ); public function getPhone(): Phone; public function setPhone( Phone $phone ); } Don’t do that Do this interface ContactDetails { public function getAddress(): string; public function setAddress( string $address, ); public function getZip(): string; public function setZip( string $zip, ); public function getCountry(): string; public function setCountry( string $country, ); public function getPhone(): string; public function setPhone( string $phoneNumber, ); }
  28. Katy Ereira - #PHPDay24 - @[email protected] interface Person { public

    function getAge(): ?int; public function setBirthDate( DateTime $birthdate, ): void; } Don’t do that Do this interface Person { public function getBirthDate(): ?DateTime; public function setBirthDate( DateTime $birthdate, ): void; public function getAge(): ?int; public function setAge(int $age): void; }
  29. Katy Ereira - #PHPDay24 - @[email protected] interface Project { public

    function isPublished(): bool; public function publish(): self; public function archive(): self; } Don’t do that Do this interface Project { public function getStatus(): Status; public function setStatus( Status $status ): void; }
  30. Katy Ereira - #PHPDay24 - @[email protected] 🍶 Acid • Check

    that use statements only reference inner layers. • Depend on abstractions, not concretions. • Don’t reference specific infrastructure. • To break rules you must first understand them. manage complexity 󰱜 Samin says, “play to each element’s strengths: use Salt to enhance, Fat to carry, and Acid to balance flavor.”
  31. Katy Ereira - #PHPDay24 - @[email protected] namespace YourApp\Domain; interface Foo

    { public function getBar(): Bar; } interface Bar {} Don’t do that Do this namespace YourApp\Domain; use YourApp\Services; interface Foo { public function getBar(): Services\Bar; } namespace YourApp\Services; class Bar {} namespace YourApp\Services; use YourApp\Domain; class Bar implements Domain\Bar {}
  32. Katy Ereira - #PHPDay24 - @[email protected] namespace MyApp\Services; use MyApp\Domain;

    class Foo implements Domain\Foo { private Domain\Repository $repo; public function getFirstBar(): Domain\Bar { return $this->repo ->getBars() ->first(); } } Don’t do that Do this namespace MyApp\Services; use MyApp\Domain; use MySQL\Connection; class Foo implements Domain\Foo { private Connection $mysql; public function getFirstBar(): Domain\Bar { return new Bar( $this->mysql->execute( ‘SELECT * FROM bars LIMIT 1’, ), ); } }
  33. Katy Ereira - #PHPDay24 - @[email protected] namespace MyApp\Infrastructure; use MyApp\Domain;

    class MySQLRepo implements Domain\Repo { public function __construct( private readonly MySQL\Connection $sql ) { } public function getBars(): array { return $this->sql->execute( ‘SELECT * FROM bars’, ); } } Don’t do that Do this namespace MyApp\Infrastructure; use MyApp\Domain; class MySQLRepo implements Domain\Repo { public function __construct() { $this->sql = new MySQL\Connection(); } public function getBars(): array { return $this->sql->execute( ‘SELECT * FROM bars’, ); } }
  34. Katy Ereira - #PHPDay24 - @[email protected] 🔥 Heat • Test

    implementations, not abstractions. • Avoid using mocks unless absolutely necessary. Use fake implementations. • Implementation details are subject to change; don’t test private methods. perform under pressure 󰱜 Samin says, “apply the right type and quantity of heat for the proper amount of time ... and you will turn out vibrant and beautiful food”
  35. Katy Ereira - #PHPDay24 - @[email protected] public function testGetBar(): void

    { $sut = new Foo(); $sut->baz = Mockery::mock(Baz::class); $baz->expects(‘check’) ->andReturnTrue(); $this->assertNotNull($sut->getBar()); } Don’t do that Do this public function testGetBar(): void { $sut = Mockery::mock(Foo::class); $bar = Mockery::mock(Bar::class); $sut->expects(‘getBar’) ->andReturn($bar); $this->assertNotNull($sut->getBar()); } public function getBar(): ?Bar { return $this->baz->check() ? new Bar() : null; }
  36. Katy Ereira - #PHPDay24 - @[email protected] public function testGetBar(): void

    { $sut = new Foo(); $sut->baz = new class implements Baz { public function check(): bool { return true; } }; $this->assertNotNull($sut->getBar()); } Don’t do that Do this public function testGetBar(): void { $sut = Mockery::mock(Foo::class); $bar = Mockery::mock(Bar::class); $sut->expects(‘getBar’) ->andReturn($bar); $this->assertNotNull($sut->getBar()); } public function getBar(): ?Bar { return $this->baz->check() ? new Bar() : null; }
  37. Katy Ereira - #PHPDay24 - @[email protected] Quick recap then that

    shallot! Onion Architecture Your application has many separate layers, accessed from the outside in. ✔ Identify the layers in your system - install Deptrac! DDD How your application grows depends a lot on the domain in which it is planted. ✔ Start from the ground for new functionality. Got Feedback? Katy Ereira @[email protected] joind.in/talk/1bb9b Clean Coding Applications taste better when you master the elements of good coding. ✔ Delete redundant comments. Refactor to use DI.