2. Software Developer, father, geek, PHP and
Node.js expert. Open-source enthusiast,
active contributor on GitHub and
StackOverflow.
Who am I?
Alessandro Minoccheri
Software developer @Flowing
@minompi
@AlessandroMinoccheri
6. How do you usually start a new Symfony Project
• Install Symfony skeleton project
• Remove demo code
• Autogenerate entities
• Start coding from controllers or autogenerate those
• Ready to develop the application
12. class PaymentController
{
public function payAction(Request $request, YourBankGateway $gateway)
{
$gateway->pay($request->get('amount'), false, false, 0, 'EUR');
}
}
13. class Payment
{
public function pay(Request $request)
{
$gateway = new YourBankGateway();
$gateway->pay($request->get('amount'), false, false, 0);
}
}
14. class User
{
public function pay(Request $request)
{
$gateway = new YourBankGateway();
$gateway->pay($request->get('amount'));
}
}
16. Problems when you are guided from the framework
• Impossible to upgrade framework and vendors.
• Cost of maintenance
• Developers are not motivated because the stack it's very old for the
reason of point 1.
• Not maintainable application because everything is coupled.
• A lot of technical debt
• Impossible to change implementation easily.
20. The hexagonal architecture divides a system into several
loosely-coupled interchangeable components, such as the
application core, the database, the user interface, test
scripts, and interfaces with other systems.
This approach is an alternative to the traditional layered
architecture. (Wikipedia)
◆
22. Coupled code
namespace AppService;
use SymfonyComponentHttpFoundationRequest;
class Payment
{
public function doPayment(Request $request)
{
$gateway = new YourBankGateway();
$gateway->pay($request->get('amount'), false, false, 0);
}
}
27. Maintainable application
• Changes in one part of an application should affect as few other
places as possible
• Adding features shouldn't require to touch any part of the code-
base
• Adding new ways to interact with the application should require as
few changes as possible
• Debugging should require as few workarounds
• Testing should be relatively easy
29. Examples
• Code related to the framework
• Code related to the specific service
• Code related to the domain
30. The important distinction from domain and infrastructure
Domain
• Models: entities, value object or others
• interfaces for boundary objects
Infrastructure
• framework
• implementations for boundary objects
• Web controllers, CLI commands
Application
• Use cases
31. Why is it so important to have
maintainable software?
◆
34. Ports are like contracts
They will not have any representation in the codebase.
There is a port for every way a use case of the application can be
invoked .
There are input and output ports.
38. Adapters definition
Adapters are the implementation of the ports because for each of
these abstract ports we need some code to make the connection
work.
39. Adapter example
class MysqlUserRepository implements UserRepository
{
/**
* @var Connection
*/
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function save(User $user): void
{//do stuff...}
public function findById(UserId $userId): User
{//do stuff...}
public function findAll(): UserCollection
{//do stuff...}
}
51. namespace AppUserInfrastructureController;
use AppUserApplicationCreateUserService;
use AppUserApplicationDTOCreateUserRequest;
use SymfonyComponentHttpFoundationJsonResponse;
use SymfonyComponentHttpFoundationRequest;
class UserController
{
public function saveAction(Request $request, CreateUserService $createUserService)
{
$createUserRequest = CreateUserRequest::create($request->request->all());
$createUserService->createUser($createUserRequest);
return new JsonResponse();
}
}
52. namespace AppUserApplication;
use …
class CreateUserService
{
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function createUser(CreateUserRequest $createUserRequest): CreateUserResponse
{
$user = User::create(
$createUserRequest->getName(),
$createUserRequest->getPassword()
);
$this->userRepository->save($user);
return CreateUserResponse::createFromUser($user);
}
}
53. When to use it?
• When you have a new project
• When you need to develop a new context into a legacy application
• always? When to use it
55. Golden rule
Boy scout rule:
Leave your code better than you found it.
When to use it
56. Where can you start from?
• Using more interfaces
• Using dependency injection
• Practice with kata projects
• Apply the architecture into a real project
When to use it
57. Next improvements
- DDD: Domain driven design
- CQRS pattern (Command Query Responsibility Segregation)
- Event sourcing
- TDD
- BDD
When to use it