Advertisement
Advertisement

More Related Content

Similar to Hexagonal architecture - message-oriented software design(20)

Advertisement
Advertisement

Hexagonal architecture - message-oriented software design

  1. HEXAGONAL ARCHITECTURE Message oriented software design By Matthias Noback
  2. ARCHITECTURE What's the problem?
  3. ! ! ! Nice app
  4. ! ! ! Sad app
  5. Your brain can't handle it M V C ?
  6. Coupling with frameworks and libraries ! ! !
  7. How do you start a new project? Pick a framework Install a skeleton project Remove demo stuff Auto-generate a CRUD controller Auto-generate an entity Done "It's a Symfony project!"
  8. That's actually outside in The boring stuff The interesting stuff
  9. Slow tests DB Browser Message queue Key- value
  10. Frameworks taught us about encapsulation
  11. Low-level API $requestContent = file_get_contents('php://input'); $contentType = $_SERVER['CONTENT_TYPE']; if ($contentType === 'application/json') { $data = json_decode($requestContent, true); } elseif ($contentType === 'application/xml') { $xml = simplexml_load_string($requestContent); ... }
  12. Nicely hides the details $data = $serializer->deserialize( $request->getContent(), $request->getContentType() );
  13. Low-level API $stmt = $db->prepare( 'SELECT * FROM Patient p WHERE p.anonymous = ?' ); $stmt->bindValue(1, true); $stmt->execute(); $result = $stmt->fetch(PDO::FETCH_ASSOC); $patient = Patient::reconstituteFromArray($result);
  14. Hides a lot of details $patient = $repository->createQueryBuilder('p') ->where('p.anonymous = true') ->getQuery() ->getResult();
  15. What about abstraction?
  16. $patient = $repository->createQueryBuilder('p') ->where('p.anonymous = true') ->getQuery() ->getResult(); Concrete Concrete Concrete
  17. $patients = $repository->anonymousPatients(); Abstract Nice
  18. Coupling to the delivery mechanism
  19. public function registerPatientAction(Request $request) { $patient = new Patient(); ! $form = $this->createForm(new RegisterPatientForm(), $patient); ! $form->handleRequest($request); ! if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($patient); $em->flush(); ! return $this->redirect($this->generateUrl('patient_list')); } ! return array( 'form' => $form->createView() ); } Request and Form are web-specific EntityManager is ORM, i.e. relational DB-specific
  20. Reusability: impossible
  21. Some functionality The web The CLI
  22. Some functionality Run it
  23. Lack of intention-revealing code
  24. data data data why? why? why?
  25. public function updateAction(Request $request) { $patient = new Patient(); ! $form = $this->createForm(new PatientType(), $patient); ! $form->handleRequest($request); ! if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($patient); $em->flush(); ! return $this->redirect($this->generateUrl('patient_list')); } ! return array( 'form' => $form->createView() ); } from the HTTP request copied into an entity then stored in the database What exactly changed?! And... why?
  26. R.A.D. Rapid Application Development B.A.D. B.A.D. Application Development
  27. Inconsistencies GET /balance/
 
 {
 "currency": "USD",
 "amount" 20
 }
 PUT /balance/
 
 {
 "currency": "EUR",
 }
 !
  28. Lack of maintainability
  29. It's hard to find out what happens where UserController" ProductController" CommentController" CategoryController" AccountController" ProfileController" ..."
  30. Web controllers are not the only actions
  31. In summary Coupling with a framework Coupling with a delivery mechanism (e.g. the web) Slow tests Lack of intention in the code
  32. THE ESSENCE of your application
  33. The essence Other things
  34. The "heart"?
  35. "The heart of software is its ability to solve domain-related problems for its users. –Eric Evans, Domain Driven Design All other features, vital though they may be, support this basic purpose."
  36. What's essential? Domain model Interaction with it Use cases
  37. What's not essential?
  38. “The database is an implementation detail” –Cool software architect
  39. The core doesn't need to know about it ! ! !
  40. ! ! ! What about interaction? !
  41. The core doesn't need to know about it ! ! !
  42. Infrastructure The world outside ! ! ! Web browser Terminal Database Messaging
  43. Mmm... layers Layers allow you to separate Layers allow you to allocate Layers have boundaries Rules for crossing
  44. Rules about communication Actually: rules about dependencies
  45. What crosses layer boundaries? Message
  46. Messages someFunctionCall( $arguments, $prepared, $for, $the, $receiver ); $command = new TypeOfMessage( $values, $for, $this, $message ); handle($command);
  47. What about the application boundary? The app Message The world outside
  48. How does an app allow incoming messages at all? By exposing input ports Routes Console commands REST endpoints A WSDL file for a SOAP API
  49. Ports use protocols for communication Each port has a language of its own
  50. Web (HTTP) CLI (SAPI, STDIN, etc.)
  51. HTTP Request Form Request Controller Entity Value object W eb port Translate the request Repository
  52. Adapters The translators are called: adapters
  53. "Ports and adapters" Ports: allow for communication to happen Adapters: translate messages from the world outside == Hexagonal architecture
  54. An example Plain HTTP message Request object POST /users/ HTTP/1.1 Host: phpconference.nl ! name=Matthias&email=matthiasn oback@gmail.com Command $command = new RegisterUser( $request->get('name'), $request->get('email') );
  55. Command $command = new RegisterUser( $request->get('name'), $request->get('email') ); Expresses intention Implies changeStand-alone Only the message
  56. class RegisterUserHandler { public function handle(RegisterUser $command) { $user = User::register( $command->name(), $command->email() ); $this->userRepository->add($user); } } Command Command handler
  57. Command Command handler A Command bus Command handler B Command handler C
  58. Change New entity (User) Calculate changes (UoW) $user = User::register( $command->name(), $command->email() ); $this->userRepository ->add($user); Insert query (SQL) INSERT INTO users SET name='Matthias', email='matthiasnoback@gmai l.com';
  59. SQL query EntityManager UnitOfWork QueryBuilder Persistence port Prepare for persistence Repository Value object Entity
  60. Messaging (AMQP) Persistence (MySQL)
  61. UserRepository (uses MySQL) RegisterUserHandler
  62. MySQL- UserRepository RegisterUserHandler Dependency inversion UserRepository interface
  63. MySQL- UserRepository RegisterUserHandler UserRepository interface InMemory- UserRepository Speedy alternative
  64. "A good software architecture allows decisions [...] to be deferred and delayed." –Robert Martin, Screaming Architecture
  65. IN CONCLUSION what did we get from all of this?
  66. Separation of concerns Core Infrastructure
  67. Command Command Command handler Command handler Stand-alone use cases Command Command handler Intention- revealing Reusable
  68. Infrastructure stand-ins Regular implementation Interface Stand-in, fast implementation
  69. This is all very much supportive of... See also: Modelling by Example DDD TDD BDD CQRS
  70. QUESTIONS? FEEDBACK? joind.in/14238
Advertisement