Presentation is loading. Please wait.

Presentation is loading. Please wait.

©Alistair Cockburn 2009 Alistair Cockburn You Can't Be Agile if Your Architecture's a Mess “Continuous attention to technical.

Similar presentations


Presentation on theme: "©Alistair Cockburn 2009 Alistair Cockburn You Can't Be Agile if Your Architecture's a Mess “Continuous attention to technical."— Presentation transcript:

1 ©Alistair Cockburn 2009 http://Alistair.Cockburn.us Alistair Cockburn You Can't Be Agile if Your Architecture's a Mess “Continuous attention to technical excellence and good design enhances agility.” (Agile Manifesto, 2001)

2 ©Alistair Cockburn 2009 2 http://Alistair.Cockburn.us go to http://Alistair.Cockburn.us/Hexagonal Architecture implement discount = amount * discountRate(amount) amount comes from User and/or Test framework discountRate (amount) from DB and/or in-memory mock Implement the “barn door” Make it simple Prepare to show your code

3 ©Alistair Cockburn 2009 3 http://Alistair.Cockburn.us Why? Up-down and Left-right maps of architecture miss an important symmetry App User App User Both user and DB are ‘outside’ the application

4 ©Alistair Cockburn 2009 4 http://Alistair.Cockburn.us Most people implement them ‘inside’ the application User GUI (with some bus.logic) APP business logic DB Boo :-( Can’t regression test Can’t make batch Can’t hook to another program Can’t isolate app when DB breaks

5 ©Alistair Cockburn 2009 5 http://Alistair.Cockburn.us Put both user and DB outside the application The “Hexagonal” architecture: Separate domain & application from ports & adapters. Domain + Application Port

6 ©Alistair Cockburn 2009 6 http://Alistair.Cockburn.us Use “adapters” to connect ports to external devices Application Adapter app Adapter

7 ©Alistair Cockburn 2009 7 http://Alistair.Cockburn.us Typical ports are input, data stores. Typical adapters are test, UI, app-app, DB, mocks Application user-side API app test harness adapter data-side API DB access service mock (in-memory) database (Use case boundary) GUI adapter HTTP adapter app-to-app adapter

8 ©Alistair Cockburn 2009 8 http://Alistair.Cockburn.us Sample application: Storm Warning System Application trigger data app wire feed test adapter http feed notifications answering machine adapter mock telephone administration database test adapter GUI app-to-app adapter DB mock database http adapter email adapter

9 ©Alistair Cockburn 2009 9 http://Alistair.Cockburn.us Implement Test+Mock first. Add UI & DB later Application test harness user interface mock database database access 1 324

10 ©Alistair Cockburn 2009 10 http://Alistair.Cockburn.us Sample Implementation [1] Test Adapter with FIT import fit.ColumnFixture; public class TestDiscounter extends ColumnFixture { private Discounter app = new Discounter(); public double amount; public double discountRate() { return app. discountRate(amount); } } Application test harness user interface mock database database access 1324

11 ©Alistair Cockburn 2009 11 http://Alistair.Cockburn.us Sample Implementation of [2] fake DB Discounter app = new Discounter(); public void actionPerformed(ActionEvent event) {... String amountStr = text1.getText(); double amount = Double.parseDouble(amountStr); discountRate = app.discountRate(amount)); text3.setText( "" + discount ); Application test harness user interface mock database database access 1324

12 ©Alistair Cockburn 2009 12 http://Alistair.Cockburn.us Sample Implementation of Replaceable Mock/DB... public interface RateRepository { double getRate(double amount); } public class RepositoryFactory { public RepositoryFactory() { super(); } public static RateRepository getMockRateRepository() {return new MockRateRepository(); }} public class MockRateRepository implements RateRepository { public double getRate(double amount) { if(amount <= 100) return 0.01; if(amount <= 1000) return 0.02; return 0.05; } Application test harness user interface mock database database access 1324

13 ©Alistair Cockburn 2009 13 http://Alistair.Cockburn.us Sample Implementation of Switchable Mock/DB import repository.RepositoryFactory; import repository.RateRepository; public class Discounter { private RateRepository rateRepository; public Discounter(RateRepository r) { super(); rateRepository = r; } public double discount(double amount) { double rate = rateRepository.getRate( amount ); return amount * rate; } import app.Discounter; import fit.ColumnFixture; public class TestDiscounter extends ColumnFixture { private Discounter app = new Discounter(RepositoryFactory.getMockRateRepository()); public double amount; public double discountRate() {return app.discountRate( amount ); } }

14 ©Alistair Cockburn 2009 14 http://Alistair.Cockburn.us Phil Borland’s ‘Riser’ Entity Framework http://code.google.com/p/riser/ Based on Apple's Core Data Framework ManagedObjectContext to fetch, delete, save objects. PersistentStore represents a single data store. e.g. SQL data store (MySQL, Postres, etc) & a custom one with mock objects.  Switch them at will and the app runs the same. contact: phil@risertech.com src: net.sf.riser.examples.documentation with package: net.sf.riser.examples.hexagonal

15 ©Alistair Cockburn 2009 15 http://Alistair.Cockburn.us Domain Objects public class Discount { private String productName; private double rate; // Getters, setters, constructors, etc } public class Discounter { public static double getDiscount(String productName) { FetchRequest fetchRequest = new FetchRequest(Constants.DISCOUNT); fetchRequest.setPredicate(Predicates. createPredicate("productName=\""+productName+"\” ”)); List discounts = getContext().executeFetchRequest(fetchRequest); if (discounts == null || discounts.size() == 0) { return 0; } else { return ((Discount) discounts.get(0)).getRate(); }

16 ©Alistair Cockburn 2009 16 http://Alistair.Cockburn.us Mock Store public class MockStore extends AbstractMockStore { public static final String TYPE = "HexagonalMockStore"; public MockStore (PersistentStoreCoordinator coordinator, String configurationName, URL location, Map options) { super(coordinator, configurationName, location, options); } public MockStore(PersistentStoreCoordinator coordinator) { super(coordinator, null, null, null); } @Override public void doLoad() { EntityDescription entity = Constants.DISCOUNT; addObject(entity, new Discount("ProductOne",.01)); addObject(entity, new Discount("ProductTwo",.02)); } @Override public String getType() { return TYPE; }

17 ©Alistair Cockburn 2009 17 http://Alistair.Cockburn.us Creating the Context private static ManagedObjectContext createContext(ManagedObjectModel model) { PersistentStoreCoordinator coordinator = new PersistentStoreCoordinator(model); if ("mock".equals(System.getProperty("store"))) { coordinator.addPersistentStore( new MockStore(coordinator)); } else { coordinator.addPersistentStore( new SQLStore(coordinator,createSQLOptions())); } ManagedObjectContext context = new ManagedObjectContext(coordinator); return context; } contact: phil@risertech.com src at: net.sf.riser.examples.documentation with package: net.sf.riser.examples.hexagonal

18 ©Alistair Cockburn 2009 18 http://Alistair.Cockburn.us Keep your design clean so you stay able to move. Make the tests the first “user” of your app. Make the first DB a mock, be able to swap them. Application user-side API app test harness adapter data-side API DB access service mock (in-memory) database (Use case boundary) GUI adapter HTTP adapter app-to-app adapter Application test harness user interface mock database database access 1324

19 ©Alistair Cockburn 2009 19 http://Alistair.Cockburn.us


Download ppt "©Alistair Cockburn 2009 Alistair Cockburn You Can't Be Agile if Your Architecture's a Mess “Continuous attention to technical."

Similar presentations


Ads by Google