XPug Milano - Hexagonal architecture
-
Upload
gabriele-tondi -
Category
Technology
-
view
737 -
download
0
Transcript of XPug Milano - Hexagonal architecture
Metwitter: @racingDeveloper mail: [email protected] work: agile software developer @ love: metodologie agili (XP), OOD, TDD… and races
"Model-View-Controller is the concept introduced by Smalltalk's inventors (Trygve
Reenskaug and others) of encapsulating some data together with its processing (the model)
and isolate it from the manipulation (the controller) and presentation (the view) part that
has to be done on a UserInterface. reason."
http://c2.com/cgi/wiki?ModelViewController
"Software architecture is not about databases, web servers, dependency injection, Rails, Hibernate, JSF, Struts, Spring, or any other
framework or tool. Architecture is about intent."
"When you see a web-based accounting system, the architecture of that system should scream accounting at you. The fact that it's a web
based system should be unnoticeable. After all, the web is just a delivery mechanism; and we don't want our system
architecture polluted with delivery mechanisms, databases, and other low level tools and concerns."
[https://cleancoders.com/episode/clean-code-episode-7/show]
public function changeUserPassword($userId, $newPassword) {
$entityManager = $this->getDoctrine()->getRepository('AppBundle:Post')->getEntityManager; $query = $entityManager->createNativeQuery('SELECT * FROM users WHERE id = ?'); $query->setParameter(1, 'xpug');
$user = $query->getResult();
if ($user->getPassword() == $newPassword) { return "You should change you password"; }
if (strlen($newPassword) < 8) { return "Password must be at least 8 chars long"; } $user->setPassword($newPassword); $entityManager->store($user);
return "Thank you, password updated”;
}
Esempio tipico
public function changeUserPassword($userId, $newPassword) {
$entityManager = $this->getDoctrine()->getRepository('AppBundle:Post')->getEntityManager; $query = $entityManager->createNativeQuery('SELECT * FROM users WHERE id = ?'); $query->setParameter(1, 'xpug');
$user = $query->getResult();
if ($user->getPassword() == $newPassword) { return "You should change you password"; }
if (strlen($newPassword) < 8) { return "Password must be at least 8 chars long"; } $user->setPassword($newPassword); $entityManager->store($user);
return "Thank you, password updated”;
}
Per quali motivi può cambiare?
public function changeUserPassword($userId, $newPassword) {
$entityManager = $this->getDoctrine()->getRepository('AppBundle:Post')->getEntityManager; $query = $entityManager->createNativeQuery('SELECT * FROM users WHERE id = ?'); $query->setParameter(1, 'xpug');
$user = $query->getResult();
if ($user->getPassword() == $newPassword) { return "You should change you password"; }
if (strlen($newPassword) < 8) { return "Password must be at least 8 chars long"; } $user->setPassword($newPassword); $entityManager->store($user);
return "Thank you, password updated”;
}
Per quali motivi può cambiare?
Single Responsibility Principle (SRP)
"Gather together the things that change for the same reasons. Separate those things that change for different reasons.”
Dependency Inversion Principle (DIP)
"High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstraction."
http://alistair.cockburn.us/Hexagonal+architecture
Application• È il cuore dell’esagono
• Contiene la logica di dominio
• È totalmente indipendente dal meccanismo di delivery (console application, REST endpoint…)
• È totalmente indipendente dai servizi di supporto (database, SMTP, AMPQ…)
Application - Dominio
• Aggregati, entità, valori, servizi di dominio, eventi di dominio vivono all’interno dell’esagono
• Vengono coordinati dagli use-case (Application Service) per compiere gli scopi dall'applicazione
Application - Porte Primarie• aka use-case, application service
• usate per guidare l’applicazione dall'esterno
• non conoscono la natura del client che le userà
• il focus è sullo scopo della conversazione, non sulla tecnologia utilizzata
esempio - portapublic class BookRatingUseCase implements UseCase{ private final BookCatalog catalog; private final BookRatingRepository bookRatingRepository; public BookRatingUseCase(BookCatalog catalog, BookRatingRepository bookRatingRepository) { this.catalog = catalog; this.bookRatingRepository = bookRatingRepository; } public void execute(BookRatingRequest request) { Book book = catalog.bookWithId(new BookId(request.getBookId())); guardBookNotFound(book); BookRating rate = book.rate(Rating.value(request.getRating())); bookRatingRepository.add(rate); } private void guardBookNotFound(Book book) { if (book == null) throw new BookNotFoundException(); } }
esempio - adapter@RestController@RequestMapping(value = "/book/{bookId}/rating") public class BookRatingController{ private final UseCase useCase; @Inject public BookRatingController(UseCase bookRatingUseCase) { this.useCase = bookRatingUseCase; } @RequestMapping(method = POST) @ResponseStatus(value = CREATED) public void rateBook(@PathVariable String bookId, @RequestBody BookRatingDTO bookRatingDTO) { useCase.execute(new BookRatingRequest(bookId, bookRatingDTO.getRating())); } @ExceptionHandler(value = BookNotFoundException.class) @ResponseStatus(value = NOT_FOUND) public void exceptionHandler(BookNotFoundException e) { }}
Application - Porte Secondarie
• usate dagli abitanti dell’esagono per comunicare con il mondo esterno
• vengono definite come interfacce
• il focus deve essere sullo scopo della conversazione, e non sulla tecnologia sottostante
esempio - adapterpublic class InMemoryBookCatalog implements BookCatalog{ private final List<Book> books; public InMemoryBookCatalog(Book... books) { this.books = asList(books); } @Override public Book bookWithId(BookId bookId) { for (Book book : books) { if (book.hasId(bookId)) return book; } return null; } }
Vantaggi• È possibile concentrarsi da subito sul dominio del problema
• Differire le decisioni riguardo I/O devices
• Testare in unità il dominio
• Erogare l’applicazione su canali differenti (rest, console, acceptance
test, stream-processing ecc)