Model Drevet Design i Praksis Dansk IT - På Vej Hjem møde d. 8/9-2009 Jeppe Cramon - TigerTeam ApS
Lidt om mig 15 års erfaring som software udvikler Partner i TigerTeam Første erfaring med model drevet udvikling i 2001 Siden erfaring med model drevet udvikling fra BEC Pension, letpension, Elektronisk Tinglysning samt et større ERP projekt med SAP integration. Facilitator for Dansk IT s Java kompetence netværk
Dansk IT Java kompetence netværk Et engageret netværk af Java professionelle Mødes 4 gange årligt Eksterne og interne foredragsholdere Debat emner Flere medlemmer vil sikre større udbytte af netværket...
Hvorfor Model Drevet Udvikling?
Fordi software udvikling stadig tager for lang tid!
Hvorfor tager det for lang tid?
Vi løser de forkerte problemer For dårlig kommunikation
Udvikling kræver stadig for store resourcer For lidt abstraktion og automatisering
Hvilke tiltag findes der til at løse disse udfordringer i dag?
Outsource eller bruge flere konsulenter
Agil udvikling
Bedre tekniske frameworks
Mere udtryksfulde programmeringssprog
Flere af disse tiltag virker fortrinligt, hvis de bruges rigtigt...
Men kan vi ikke bare få forretningen til at kode og fyre programmørerne? ;) Kommer ALDRIG til at ske!
Men vi kan få forretningen til at deltage mere aktivt i udviklingen, gennem...
Model Driven Software Development (MDSD)
MDSD er brugen af modeller til at...
Forenkle virkeligheden
Forbedre kommunikationen
Hæve abstraktions niveau
Forbedre kvaliteten
Der findes to forskellige typer modeller...
Tekstuelle modeller Domain Specific Languages (DSL)
Visuelle modeller
Fællesnævneren er...
Godt supplement til eksisterende metoder
Et højere udtryks- og abstraktions niveau
En elektrificeret beskrivelse af problem domænet
Er MDSD ikke bare CASE tools om igen?
NEJ - CASE tools betød One size fits all Generatoren var oftest låst af leverandøren Gav en 80-20 med 20-80 løsning Det var svært at ændre/tweake toolet MDSD handler om Pragmatisme - One Model Doesn t Fit All! Brug den model der hvor det giver mening for dit projekt Brug modeller til det de er gode til - skriv resten i hånden Brug en effektiv generator og skriv dine egne udvidelser - No lock in Fokus på 80-20 uden 20-80 Tillad specialisering på alle niveauer
Eksempler på anvendelse af MDSD
1: Advanced ebusiness applikation Business Engine Skrevet i C Custom Metadata SQL + DB2 Service kald JDBC Schema Metadata Admin Applikation +JSP Java Persistence Skrevet i Java klasser (CMP)
Konklusion på JSP/Template baseret og DB drevet MDSD Det virkede Java gruppen var agil, da den nemt kunne reagerer på DB ændringer DB ens Schema MetaData havde begrænset indhold => behov for ekstra meta data Ekstra meta data forårsagede behov for Web app til at holde dem ved lige JSP erne blev rodede, det var svært at tilføje ortogonale udvidelser Hang ikke sammen med build systemet
2: Forsikrings domæne model Opgaven var: Byg et nyt Java baseret pensionskasse system (GALOP) til flere samtidige pensionskasser med vidt forskellige og komplicerede pensions produkter Skulle bygges op omkring Spring Framework (Teknisk infrastruktur framework) Hibernate/JPA (Objekt-Relationel Mapping framework) bidrog med Process, Metode samt Teknisk ekspertise indenfor JEE, Spring, Hibernate og Web bidrog med Pensions domæne viden samt ekspertise i software og model drevet udvikling
Løsning Pensionskasse X Model Pensionskasse Y Model Høj niveau beskrivelse af domænet vha. UML modeller GALOP Pensions Model Lagdelte domæne modeller med specialisering Model til Model transformation af de generelle modeller til den pågældende løsning (modellen er generel, implementationen er specifik) Dansk Pensions Model En-vejs iterativ kode generering vha. plugin baseret kode generator Generel Pensions Model
Højniveau beskrivelse af domænet UML:
Højniveau: Bitemporal historik Giver frihed til at vælge den rette implementation uden at afsløre det i modellen 1 * 1 1
Højniveau: Versionering (Temporal Object Pattern)
Konklusion på UML baseret domæne model Det virker RIGTIG godt til domæne modeller Super kommunikations form - Teknikken støjer ikke Agilitet: Remodellerer når der er behov og regenererer Learn as you go along: Når vi blev klogere på Hibernate udfordringer var det nemt at rette det gennem generatoren. Lille indsats => Stor effekt Ensartet kvalitet Stor fleksibilitet i hele processen
3: SOA model Opgaven var: BEC/Schantz forsikringskerne skulle specialiseres til letpension Forsikringskernen skulle integreres i en lagdelt SOA model med tilhørende orkestrering SOAP / WSDL / XML Schema (WS-I Basic) - Aka. the usual suspects!
Løsning: Beskrivelse af service grænsefladen vha. UML En-vejs iterativ kode generering af WSDL XML Schema Serverside service skelet Klient stub (for test) HTML beskrivelse af forskellene mellem to model versioner
Service grænseflade i UML
Konklusion på UML baseret SOA model Det virkede så godt at det gik fra kun at være forsikrings projektet der brugte det til det var hele letpensions SOA model der blev beskrevet i UML og genereret Super kommunikations form - Teknikken støjer ikke Agilitet: Remodellerer når der er behov og regenererer - Mulighed for at lave automatiseret dokumentation af model ændringer mellem versioner Adaptability: Når service principperne ændrede sig (i starten) var forsikrings projektet oftest 10 gange så hurtigt til at rette servicene til (i forhold til de andre projekter) Ensartet kvalitet Stor fleksibilitet i hele processen
4: Elektronisk tinglysning Opgaven var: Få Elektronisk tinglysning i mål så hurtigt som muligt TigerTeam deltog i at få den meget komplekse domæne model persisteret vha. Hibernate/JPA (Objekt-Relationel Mapping framework) Oracle 10.x TigerMDSD (Model baseret kode generator)
Krav vi skulle kunne leve op til Oracle har en begrænsning på 30 karakterer for tabeller, kolonner, etc. Det skal være muligt at udvide den genererede kode uden at tabe udvidelserne ved regenerering Automatiseret integrations test af den genererede kode op mod Oracle databasen Fast definerede regler (defaults) der skal kunne overstyres enten via generatoren eller via modellen
Hvordan laver man en generel og fleksibel generator der kan leve op til disse krav?
TigerMDSD proces for kode generering + UML modellering TigerMDSD XMI Eksport konfiguration MODEL is KING Java/C# kode JPA konfiguration Database konfiguration Integrations test Test data generator WSDL XML Schema...
Trin 1 MagicDraw + XmiReader XMI UML Model object graf
TigerMDSD - På UML Model Niveauet XmiReader reader = new EAXmiReader(); XmiReader reader = new MagicDrawXmiReader(); MetaModel metamodel = reader.read("model.xml");
Transformation - Fra UML model til Resultat UML Model object graf Transformation (TigerMDSD konfiguration) Java/C# kode JPA konfiguration Database konfiguration Integrations test Test data generator WSDL XML Schema...
TigerMDSD - Transformation til Java JavaGenerator javagenerator = new JavaGenerator(); List<ClazzOrInterface> allgeneratedclazzes = javagenerator.execute(metamodel); Meta Type MetaPackage MetaClazz MetaAssociationClazz MetaEnumeration MetaInterface MetaProperty MetaOperation Java Model JavaPackage Clazz Clazz Enumeration Interface Property (Består af Field, GetterMethod og SetterMethod) Method
TigerMDSD - Java Model
TigerMDSD - Udvidelser baseret på Events
TigerMDSD - Udvidelser public interface GeneratorEventListener { boolean handle(generatorevent event);... }
TigerMDSD - Eksempel på udvidelse @OneToMany @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) private Set<Tire> tires; public class HibernateDeleteOrphanListener extends BaseJpaGeneratorEventListener { @Override protected boolean handleonetomanyownerofassociation(onetomanyassociationevent event) { } if (isdeleteorphancandidate(event)) { event.getproperty().getfield().addannotations( new Annotation(Cascade.class).addAnnotationAttribute("value", CascadeType.DELETE_ORPHAN) ); event.getproperty().removesettermethod(); } return true; } protected boolean isdeleteorphancandidate(onetomanyassociationevent event) {... }
TigerMDSD - Eksempel på udvidelse protected boolean isdeleteorphancandidate(onetomanyassociationevent event) { } if (event.getmetaproperty().isownerofassociation() &&!event.getmetaproperty().getassociation().isbidirectional() && }!event.getmetaproperty().getassociation().isselfreferencing()) { // Check the clazz of the opposite property to see what kind of associations it has for (MetaProperty submetaproperty : event.getmetaproperty().gettype().getproperties()) { if (submetaproperty.ispartinanassociation()) { } } if (submetaproperty.isownerofassociation()) { if (submetaproperty.getassociationtype() == AssociationType.ManyToMany } submetaproperty.getassociationtype() == AssociationType.OneToMany) { return false; } } else if (submetaproperty.getassociation().isbidirectional()) { return true; return false; // The type of the our sub property is not an owning association and we have // a java association in both directions (bidirectional), which hibernate doesn't handle return false;
TigerMDSD - Indbyggede udvidelser Built-in Types Bidirectional associations Property Sugar methods Get Or New Property methods Constructor (immuteable properties) Class Hierarchy Java doc generator Serial Version UID generator MetaType Java doc generator Serializable Pojo s ToString/Equals/HashCode JPA Field based persistence JPA Named Tables and Columns Hibernate Foreignkey Constraints Hibernate Foreignkey Index Hibernate Fetch Optimization Hibernate Association Unproxying Hibernate Table Comments Hibernate HH-3544 bug fix
TigerMDSD - Java konfiguration JavaGenerator javagenerator = new JavaGenerator(); javagenerator.addeventlisteners(new BuiltInTypesListener() { @Override protected void resolvebuiltintypes(type type) { if (type.getname().equalsignorecase("datetime")) { type.setwrappedjavaclass(datetime.class); } else { super.resolvebuiltintypes(type); } } }); javagenerator.addeventlisteners(new JPAGeneratorEventListener().setShouldMakeBaseClazzesMappedSuperClassesIfPossible(true).setShouldGeneratePresentFieldInEmbeddables(false).setDefaultToLazyFetchingForAllAssociations(true) ); javagenerator.addeventlisteners(new JPANamedTablesAndColumnsListener()); javagenerator.addeventlisteners(new BidirectionalGeneratorEventListener()); javagenerator.addeventlisteners(new HibernateAssociationUnproxyListener()); javagenerator.addeventlisteners(new SerialVersionUIDGeneratorListener()); javagenerator.addeventlisteners(new PropertySugarMethodsEventListener()); javagenerator.addeventlisteners(new MetaTypeJavaDocListener()); javagenerator.addeventlisteners(new HibernateIndexingListener()); javagenerator.addeventlisteners(new HibernateDeleteOrphanListener()); javagenerator.addeventlisteners(new HibernateValidatorNotNullListener()); javagenerator.setcreateextensionclazzes(true);
TigerMDSD - YAML konfiguration
3 level inheritance - Udvikler udvidelser Hvad vi modellerer: Hvad vi genererer: Optionel Alternativer: Partial Classes Mixins / Traits Priviledged Aspects Protected Regions
Best practices
Tak for opmærksomheden! For mere information jeppe@tigerteam.dk