Retour à la simplicité

22
Retour à la simplicité Vincent Tencé @testinfected http://vtence.com http://github.com/testinfected

Transcript of Retour à la simplicité

Page 1: Retour à la simplicité

Retour à la simplicité

Vincent Tencé@testinfected

http://vtence.comhttp://github.com/testinfected

Page 2: Retour à la simplicité

Un sentiment de déja-vu ?

Vous démarrez un projet avec une dizaine de librairies et frameworks

Le bagage à trainer est lourd et vous coute cher

Un des frameworks se met en travers de votre chemin

Vous vous sentez prisonnier d’un des frameworks

Un framework vous surprend par sa « magie »

Page 3: Retour à la simplicité
Page 4: Retour à la simplicité
Page 5: Retour à la simplicité

Le point de départ

• Spring et Spring MVC

• Velocity et SiteMesh

• Hibernate, JPA

• Hibernate Validator

• Maven

• 53 jars externes !

Page 6: Retour à la simplicité
Page 7: Retour à la simplicité

Le défi

• Uniquement des outils simples

• DIYS (Do It Yourself Simply)

• Assemblage, déploiement et configuration faciles

• XML

• Annotations

• Framework (Web, ORM, DI)

Page 8: Retour à la simplicité
Page 9: Retour à la simplicité
Page 10: Retour à la simplicité
Page 11: Retour à la simplicité

Build

define 'petstore', [..] do define 'domain' do compile.with test.with HAMCREST package :jar end

define 'persistence' do compile.with project(:domain) test.with HAMCREST, :flyway, :mysql, NO_LOG, [...] package :jar end

define 'webapp' domain compile.with :simpleframework, :jmustache, [...] test.with HAMCREST, :antlr_runtime, :cssselectors, :hamcrest_dom, [...] test.with transitive(artifacts(:nekohtml, :htmlunit, :jmock_legacy))

package :jar end

Page 12: Retour à la simplicité

Injection de dépendances

AttachmentStorage attachments = new FileSystemPhotoStore("/photos");Connection connection = new ConnectionReference(request).get();Transactor transactor = new JDBCTransactor(connection);ProductCatalog products = new ProductsDatabase(connection);ItemInventory items = new ItemsDatabase(connection);OrderBook orders = new OrdersDatabase(connection);ProcurementRequestHandler procurement =

new PurchasingAgent(products, items, transactor)OrderNumberSequence orderNumbers = new OrderNumberDatabaseSequence(connection);Cashier cashier = new Cashier(orderNumbers, orders, transactor);Messages messages =

new BundledMessages(ResourceBundle.getBundle("ValidationMessages"))

Page 13: Retour à la simplicité

Routing

Router router = Router.draw(new DynamicRoutes() {{ get("/products").to(new ListProducts(products, attachments, pages.products())); post("/products").to(new CreateProduct(procurement)); get("/products/:product/items").to(new ListItems(items, pages.items())); post("/products/:product/items").to(new CreateItem(procurement)); get("/cart").to(new ShowCart(cashier, pages.cart())); post("/cart").to(new CreateCartItem(cashier)); get("/orders/new").to(new Checkout(cashier, pages.checkout())); get("/orders/:number").to(new ShowOrder(orders, pages.order())); post("/orders").to(new PlaceOrder(cashier)); delete("/logout").to(new Logout()); map("/").to(new StaticPage(pages.home()));}});

Page 14: Retour à la simplicité

MVC

public class ShowOrder implements Application { private final OrderBook orderBook; private final Page orderPage;

public ShowOrder(OrderBook orderBook, Page orderPage) { this.orderBook = orderBook; this.orderPage = orderPage; }

public void handle(Request request, Response response) throws Exception { String number = request.parameter("number"); Order order = orderBook.find(new OrderNumber(number)); orderPage.render(response, context().with("order", order).asMap()); }}

Page 15: Retour à la simplicité

Accès aux données

public class OrdersDatabase implements OrderBook { private final Connection connection; [...]

public OrdersDatabase(Connection connection) { this.connection = connection; }

private List<LineItem> findLineItemsOf(Order order) { return Select.from(lineItems). where("order_id = ?", idOf(order).get()). orderBy("order_line"). list(connection); }

private Order findOrder(OrderNumber orderNumber) { return Select.from(orders, "_order"). leftJoin(payments, "payment", "_order.payment_id = payment.id"). where("_order.number = ?", orderNumber). first(connection); }

Page 16: Retour à la simplicité

Transactions

public class Cashier implements SalesAssistant { [...]

public OrderNumber placeOrder(PaymentMethod paymentMethod) throws Exception { Ensure.valid(paymentMethod); QueryUnitOfWork<OrderNumber> order = new QueryUnitOfWork<OrderNumber>() { public OrderNumber query() throws Exception { OrderNumber nextNumber = orderNumberSequence.nextOrderNumber(); final Order order = new Order(nextNumber); order.addItemsFrom(cart); order.pay(paymentMethod); orderBook.record(order); cart.clear(); return nextNumber; } }; return transactor.performQuery(order); }

Page 17: Retour à la simplicité

Contraintes de validité

public class CreditCardDetails extends PaymentMethod implements Serializable {

private final CreditCardType cardType; private final Constraint<String> cardNumber; private final NotNull<String> cardExpiryDate; private final Valid<Address> billingAddress;

public CreditCardDetails(CreditCardType type, String number, String expiryDate, Address billingAddress) {

this.cardType = type; this.cardNumber = Validates.both(Validates.notEmpty(number),

Validates.correctnessOf(type, number)); this.cardExpiryDate = Validates.notNull(expiryDate); this.billingAddress = Validates.validityOf(billingAddress); }

Page 18: Retour à la simplicité

Validation

public class Validator {

public <T> Set<ConstraintViolation<?>> validate(T target) { Valid<T> valid = Validates.validityOf(target); valid.disableRootViolation(); ViolationsReport report = new ViolationsReport(); valid.check(Path.root(target), report); return report.violations(); }

[...]}

Page 19: Retour à la simplicité

Formulaires

public class PaymentForm extends Form {

public static PaymentForm parse(Request request) { return new PaymentForm(new CreditCardDetails( valueOf(request.parameter("card-type")), request.parameter("card-number"), request.parameter("expiry-date"), new Address(request.parameter("first-name"), request.parameter("last-name"), request.parameter("email")))); }

private final Valid<CreditCardDetails> paymentDetails;

public PaymentForm(CreditCardDetails paymentDetails) { this.paymentDetails = Validates.validityOf(paymentDetails); }

public CreditCardType cardType() { return paymentDetails().getCardType(); } public CreditCardDetails paymentDetails() { return paymentDetails.get(); }}

Page 20: Retour à la simplicité

Toutefois

• Pas très « entreprise »

• Pas à la portée de toutes les équipes ?

• Pas à toutes les sauces

• Pas sans risque ?

Page 21: Retour à la simplicité

Leçons apprises

• Éviter la tentation des frameworks

• Utiliser des outils simples, légers et spécialisés

• Construire mes propres outils

• S’inspirer des meilleures idées; réécrire le code simplement

• Spécialiser plutôt que de généraliser

Page 22: Retour à la simplicité

Références

•Nouvelle version :https://github.com/testinfected/simple-petstore

• Ancienne version :https://github.com/testinfected/petstore

• Data Mapping :https://github.com/testinfected/tape