Symfony and Hexagonal Architecture

In this article, we will briefly go over the theory and in practice we will figure out how to translate any Legacy application to a hexagonal architecture. The narration will be in the context of the Symfony framework and PHP 7.4, but the syntax of the examples given is so simple that you can easily understand how to do the same in your programming language (if it supports OOP).







During my career I have worked on many Symfony projects, and one of the most frequent problems that clients call our company with is that their software is "locked" by an old version of the framework or it has become unattended because it is finding and fixing bugs is too expensive.







Usually, I try to get a good understanding of why these legacy projects are in this state. And often I found a common pattern: the team at the beginning of the project needs to quickly create an application from scratch, because the deadlines are tight.







Their development process begins something like this:







  • install symfony skeleton project using composer
  • remove demo code
  • auto-generation of models
  • auto-generation of controllers
  • now everything is ready for developing (business logic) application


, - , - .







, flow .







, , Symfony ( ) , ​​ , β€” Β«domainΒ», .







, , , :







  • ,
  • ,


, , : .









- , -, 2005 .







, ​​, , , . . ()







, : ?







, , , , . , , .









, 10 , PHP, .







PHP , composer-, -, .







, .







β€” (not maintainable).







.







, , , -.







, . .







Β« Β» β€” , - .







, , , , , .







, , , .







, , , .







/ , , .







:









class Payment
{
  public function pay(Request $request): void
  {
     $gateway = new YourBankGateway();
     $gateway->pay($request->get('amount'));
  }
}

      
      





, :







  • pay Request, - HTTP. , , , - .
  • YourBankGateway , , , , .


:









interface GatewayProvider {
    public function pay(Money $amount): void;
}

class YourBankGateway implements GatewayProvider {
    public function pay(Money $amount): void
    {
        //do stuff..
    }
}

class Payment {
    private GatewayProvider $gateway;

    public function __construct(GatewayProvider $gateway)
    {
        $this->gateway = $gateway;
    }

    public function payThroughGateway(Money $amount): void
    {
        $this->gateway->pay($amount);
   }
}

      
      





, , .







: Payment - HTTP , Money ( DTO) Request.







, .











«» () .







β€” , () , .







β€” , , .







?







  • () -


, .











, : , :







  • ,
  • ,
  • , API


, : , .







:







  • : , …


:







  • ( )


:







  • , CLI


?



. β€œ -” .







, .







, .









, .







( UI, API ..), (, . .). .







β€” .







:









interface ProductRepositoryInterface
{
   public function find(ProductId $id): ?Product;
}

      
      





β€” , . , .









β€” , , .







.







:









class MysqlProductRepository implements ProductRepositoryInterface
{
    private $repository;

    public function __construct(ProductRepository $repository)
    {
        $this->repository = $repository;
    }

    public function find(ProductId $id): ?Product
    {
        return $this->repository->find(id);
    }
}

      
      





.













, CLI HTTP-, . .







, , , .







, PHP :













: Payment Cart.







, . , .







- ( , UUID ramsey/uuid).







, .







.







, .











, , .







, . .













, .







, .







.









, :







  • , , . unit-.
  • , , , .
  • composer-, , . ., . , .
  • , .




, , , .







?



, , , .







, Infrastructure Domain.







.







, , pull- .







legacy , , :







, .







Let’s Make Our Projects Great Again



:







  • DDD (Domain-driven design)
  • CQRS ( )
  • Event sourcing
  • TDD
  • BDD


, .








All Articles