Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building a Lean Architecture for Web Applications using Domain Driven Design & Model Driven Software Development

4,523 views

Published on

How do we create a lean architecture and process for building Web Applications?

In this talk I will give my view on how I believe we can improve our track record - exemplified through a desktop style Web application developed using the Vaadin component based web framework.

I will draw upon best practices from Domain Driven Design and Model Driven Software Development, to show how we can get closer to the users mental model, while saving precious code lines through automation.

Finally we will look at how strong data-binding can give us less error prone code and finally we will dive into the differences between MVC (Model View Controller), Model View Presenter (MVP) and finally Model View - ModelView.

Published in: Technology, Business

Building a Lean Architecture for Web Applications using Domain Driven Design & Model Driven Software Development

  1. 1. Building a lean architecture for Web Applications using Domain Driven Design & Model Driven Software Development Java Gruppen Conference - 2010 Jeppe Cramon - TigerTeam ApS TIGERTEAM ® LEAN THINKING
  2. 2. A little about me
  3. 3. A little about me 15 years as a software developer
  4. 4. A little about me 15 years as a software developer Partner in TigerTeam - focusing on our tool sets (TigerMDSD)
  5. 5. A little about me 15 years as a software developer Partner in TigerTeam - focusing on our tool sets (TigerMDSD) Facilitator for the Danish IT Societies Java competence network
  6. 6. How do we create a Lean architecture
  7. 7. How do we create a Lean architecture • We continuously think about how to improve our process, our architecture and our code
  8. 8. How do we create a Lean architecture • We continuously think about how to improve our process, our architecture and our code • We must eliminate waste - such as superfluous code
  9. 9. How do we create a Lean architecture • We continuously think about how to improve our process, our architecture and our code • We must eliminate waste - such as superfluous code • We must automate what can be automated
  10. 10. How do we create a Lean architecture • We continuously think about how to improve our process, our architecture and our code • We must eliminate waste - such as superfluous code • We must automate what can be automated • We must strive to be better at understand requirements and deliver then timely
  11. 11. How do we create a Lean architecture • We continuously think about how to improve our process, our architecture and our code • We must eliminate waste - such as superfluous code • We must automate what can be automated • We must strive to be better at understand requirements and deliver then timely • And we keep doing it - over and over again - iteratively refining our tools
  12. 12. How can Domain Driven Design help?
  13. 13. Challenge: It still takes too long time to develop software
  14. 14. Why?
  15. 15. We often solve the wrong problems
  16. 16. We often solve the wrong problems Because of poor communication
  17. 17. The developer needs to gain knowledge about the application domain to add value to a users activities
  18. 18. The amount of knowledge can be great and the complexity can be high
  19. 19. Models to the rescue
  20. 20. Models to the rescue A model is a selectively simplified and deliberately structured form of knowledge
  21. 21. Models are not about realism
  22. 22. Models are not about realism Even in a domain of real-world things, our domain model is artificial creation, like movie making...
  23. 23. Core principles of Domain Driven Design
  24. 24. Core principles of Domain Driven Design 1. The model and the heart of the design shape each other. It is the link/binding between model and implementation that makes the model relevant and ensures that the analysis that went into it applies to the final product, a running program.
  25. 25. Core principles of Domain Driven Design 1. The model and the heart of the design shape each other. It is the link/binding between model and implementation that makes the model relevant and ensures that the analysis that went into it applies to the final product, a running program. 2. The model is the backbone of a ubiquitous language used by all team members. Because of model/implementation binding developers can talk in this language. They can talk to domain experts without translating.
  26. 26. Core principles of Domain Driven Design 1. The model and the heart of the design shape each other. It is the link/binding between model and implementation that makes the model relevant and ensures that the analysis that went into it applies to the final product, a running program. 2. The model is the backbone of a ubiquitous language used by all team members. Because of model/implementation binding developers can talk in this language. They can talk to domain experts without translating. 3. The model is distilled knowledge. A model captures how we choose to think about the domain as we select terms, break down concepts and relate them
  27. 27. So how do we go about Developing the Model?
  28. 28. Write it by hand, draw it on paper, use UML, DSLs or ...?
  29. 29. Use what works best for the project - but focus on enabling automation
  30. 30. What would you prefer?
  31. 31. This?
  32. 32. Or this?
  33. 33. @javax.persistence.Basic @javax.persistence.Column(name = "comment", nullable = false) private String comment; package dk.tigerteam.mdsd.demo.model.internal; package dk.tigerteam.mdsd.demo.model.internal; @javax.persistence.Basic @javax.persistence.Entity @javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP) @javax.persistence.Table(name = "Customer") @javax.persistence.Entity @javax.persistence.Column(name = "time", nullable = false) public class Customer extends dk.tigerteam.mdsd.demo.model.AbstractEntity { private java.util.Date time; private static final long serialVersionUID = 2098912667L; @javax.persistence.Table(name = "Address") @javax.persistence.Basic @javax.persistence.Basic public class Address extends dk.tigerteam.mdsd.demo.model.AbstractEntity { @javax.persistence.Column(name = "timeslot", nullable = false) @javax.persistence.Column(name = "name", nullable = false) private static final long serialVersionUID = 1697028161L; private int timeslot; private String name; @javax.persistence.ManyToOne(cascade = { @javax.persistence.OneToOne(cascade = { javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH} javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH} @javax.persistence.Basic , fetch = javax.persistence.FetchType.LAZY) , fetch = javax.persistence.FetchType.LAZY) @javax.persistence.Column(name = "street", nullable = false) @javax.persistence.JoinColumn(nullable = false, name = "customerId") @javax.persistence.JoinColumn(name = "addressId") private dk.tigerteam.mdsd.demo.model.internal.Customer customer; @org.hibernate.validator.NotNull private String street; private dk.tigerteam.mdsd.demo.model.internal.Address address; public String getComment() { return comment; @javax.persistence.OneToMany(cascade = { @javax.persistence.Basic } javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH} , targetEntity = dk.tigerteam.mdsd.demo.model.internal.Booking.class, mappedBy = "customer", fetch = javax.persistence.FetchType.LAZY)public void setComment(String parameter) { @javax.persistence.Column(name = "zipCode", nullable = false) private java.util.Set<dk.tigerteam.mdsd.demo.model.internal.Booking> bookingCollection = new java.util.HashSet<dk.tigerteam.mdsd.demo.model.internal.Booking>(); this.comment = parameter; private String zipCode; } public String getName() { public java.util.Date getTime() { return name; return time; @javax.persistence.Basic } } @javax.persistence.Column(name = "city", nullable = false) public void setName(String parameter) { private String city; public void setTime(java.util.Date parameter) { this.name = parameter; this.time = parameter; } } public dk.tigerteam.mdsd.demo.model.internal.Address getAddress() { public String getStreet() { public int getTimeslot() { if (address instanceof org.hibernate.proxy.HibernateProxy) { return street; return timeslot; address = (dk.tigerteam.mdsd.demo.model.internal.Address) ((org.hibernate.impl.SessionImpl) ((org.hibernate.proxy.HibernateProxy) address) } .getHibernateLazyInitializer() } .getSession()).getPersistenceContext() public void setTimeslot(int parameter) { .unproxyAndReassociate(address); this.timeslot = parameter; } public void setStreet(String parameter) { } return address; public dk.tigerteam.mdsd.demo.model.internal.Customer getCustomer() { this.street = parameter; } if (customer instanceof org.hibernate.proxy.HibernateProxy) { } customer = (dk.tigerteam.mdsd.demo.model.internal.Customer) ((org.hibernate.impl.SessionImpl) ((org.hibernate.proxy.HibernateProxy) customer) public void setAddress( .getHibernateLazyInitializer() dk.tigerteam.mdsd.demo.model.internal.Address parameter) { .getSession()).getPersistenceContext() this.address = parameter; .unproxyAndReassociate(customer); public String getZipCode() { } } return zipCode; public java.util.Set<dk.tigerteam.mdsd.demo.model.internal.Booking> getBookingCollection() { } return customer; return new dk.tigerteam.foundation.bean.bidirectional.OneToManySetWrapper<dk.tigerteam.mdsd.demo.model.internal.Customer, } dk.tigerteam.mdsd.demo.model.internal.Booking>((dk.tigerteam.mdsd.demo.model.internal.Customer) this, bookingCollection) { @Override public void setCustomer( public void setZipCode(String parameter) { dk.tigerteam.mdsd.demo.model.internal.Customer parameter) { protected dk.tigerteam.mdsd.demo.model.internal.Customer getOneSideObjectInManySideObject( this.zipCode = parameter; new dk.tigerteam.foundation.bean.bidirectional.ManyToOneWrapper<dk.tigerteam.mdsd.demo.model.internal.Customer, dk.tigerteam.mdsd.demo.model.internal.Booking manySideObject) { dk.tigerteam.mdsd.demo.model.internal.Booking>((dk.tigerteam.mdsd.demo.model.internal.Booking) this) { return manySideObject.getCustomer(); @Override } } protected void addManySideObjectToOneSideCollection( dk.tigerteam.mdsd.demo.model.internal.Customer oneSide, @Override public String getCity() { dk.tigerteam.mdsd.demo.model.internal.Booking manySide) { protected void setOneSideObjectInManySideObject( ((dk.tigerteam.foundation.bean.WrappedSet<dk.tigerteam.mdsd.demo.model.internal.Booking>) oneSide dk.tigerteam.mdsd.demo.model.internal.Booking manySideObject, .getBookingCollection()).getWrappedCollection() return city; dk.tigerteam.mdsd.demo.model.internal.Customer oneSideObject) { manySideObject.setCustomer(oneSideObject); .add(manySide); } } } }; @Override } protected void removeManySideObjectFromOneSideCollection( public void setCity(String parameter) { public dk.tigerteam.mdsd.demo.model.internal.Customer withName(String name) { dk.tigerteam.mdsd.demo.model.internal.Customer oneSide, this.city = parameter; dk.tigerteam.mdsd.demo.model.internal.Booking manySide) { setName(name); } ((dk.tigerteam.foundation.bean.WrappedSet<dk.tigerteam.mdsd.demo.model.internal.Booking>) oneSide .getBookingCollection()).getWrappedCollection() return (dk.tigerteam.mdsd.demo.model.internal.Customer) this; .remove(manySide); } } public dk.tigerteam.mdsd.demo.model.internal.Address withStreet( public dk.tigerteam.mdsd.demo.model.internal.Customer withAddress( String street) { @Override dk.tigerteam.mdsd.demo.model.internal.Address address) { protected dk.tigerteam.mdsd.demo.model.internal.Customer getOneSideObjectInManySideObject( setAddress(address); dk.tigerteam.mdsd.demo.model.internal.Booking manySide) { setStreet(street); return ((dk.tigerteam.mdsd.demo.model.internal.Booking) manySide).customer; return (dk.tigerteam.mdsd.demo.model.internal.Customer) this; } } return (dk.tigerteam.mdsd.demo.model.internal.Address) this; @Override public dk.tigerteam.mdsd.demo.model.internal.Customer addToBookingCollection( protected void setOneSideObjectInManySideObject( } dk.tigerteam.mdsd.demo.model.internal.Booking... values) { dk.tigerteam.mdsd.demo.model.internal.Booking manySide, getBookingCollection().addAll(java.util.Arrays.asList(values)); dk.tigerteam.mdsd.demo.model.internal.Customer oneSide) { ((dk.tigerteam.mdsd.demo.model.internal.Booking) manySide).customer = oneSide; public dk.tigerteam.mdsd.demo.model.internal.Address withZipCode( return (dk.tigerteam.mdsd.demo.model.internal.Customer) this; } } }.updateOneSideObject(parameter); String zipCode) { public dk.tigerteam.foundation.bean.mdsd.runtime.RuntimeMetaClazz getMetaType() { } setZipCode(zipCode); return metaType; public dk.tigerteam.mdsd.demo.model.internal.Booking withComment( } String comment) { } setComment(comment); return (dk.tigerteam.mdsd.demo.model.internal.Address) this; } return (dk.tigerteam.mdsd.demo.model.internal.Booking) this; } public dk.tigerteam.mdsd.demo.model.internal.Booking withTime( public dk.tigerteam.mdsd.demo.model.internal.Address withCity(String city) { java.util.Date time) { setCity(city); setTime(time); Or this? return (dk.tigerteam.mdsd.demo.model.internal.Booking) this; } return (dk.tigerteam.mdsd.demo.model.internal.Address) this; } public dk.tigerteam.mdsd.demo.model.internal.Booking withTimeslot( int timeslot) { setTimeslot(timeslot); public dk.tigerteam.foundation.bean.mdsd.runtime.RuntimeMetaClazz getMetaType() { return (dk.tigerteam.mdsd.demo.model.internal.Booking) this; return metaType; } } public dk.tigerteam.mdsd.demo.model.internal.Booking withCustomer( } dk.tigerteam.mdsd.demo.model.internal.Customer customer) { setCustomer(customer); return (dk.tigerteam.mdsd.demo.model.internal.Booking) this; }
  34. 34. Software development still requires too many resources
  35. 35. Software development still requires too many resources Too little abstraction and automation!
  36. 36. Too many developers reinvent the wheel time and time again - or keep writing the same code over and over - when it could be handled by tools
  37. 37. Things often get too technical and the domain experts are left out of the discussion
  38. 38. Let’s get the Model back into the driving seat using Model Driven Software Development (MDSD)
  39. 39. Let’s get cranking with visual models
  40. 40. Design case Web based desktop like veterinarian clinical application
  41. 41. But first - a word form our DDD sponsor
  42. 42. Core patterns in Domain Driven Design Layered Architecture UI Layer GWT + Modules Entities Application Layer Value Objects Aggregates Domain Layer Repositories Factories Infrastructure Layer Services
  43. 43. UI/Presentation Layer Responsibilities: Show information to the user and interpret the users commands.
  44. 44. Application Layer Responsibilities: A thin layer - which is responsible for coordinating user tasks and for delegating to the domain layer. Contains no business state!
  45. 45. Domain/Model Layer Responsibilities: Representing business concepts, state and rules. Delegates storing/fetching to the Infrastructure layer!
  46. 46. Domain/Model Layer Responsibilities: Representing business concepts, state and rules. Delegates storing/fetching to the Infrastructure layer!
  47. 47. Infrastructure Layer Responsibilities: Support for the higher layers Examples: Message sending, Persistence, UI Widget, Layer interaction
  48. 48. Core patterns in Domain Driven Design Layered Architecture Modules Entities Value Objects Aggregates Repositories Factories Services
  49. 49. Core patterns in Domain Driven Design Layered Architecture Modules Entities Customer Value Objects - name : String Aggregates Repositories Factories Services
  50. 50. Core patterns in Domain Driven Design Layered Architecture Modules Entities Value Objects Aggregates Repositories Factories Services
  51. 51. Core patterns in Domain Driven Design Layered Architecture Customer Modules - name : String Entities Value Objects Aggregates Repositories Factories Services
  52. 52. Core patterns in Domain Driven Design Layered Architecture Customer Modules - name : String Entities Value Objects Aggregates 1 Repositories Address Factories - street : String Services - zipCode : String
  53. 53. Core patterns in Domain Driven Design Layered Architecture Modules Entities Value Objects Aggregates Repositories Factories Services
  54. 54. Core patterns in Domain Driven Design Layered Architecture Invoice Modules Entities Value Objects Aggregates * Repositories InvoiceLine Factories Services
  55. 55. Core patterns in Domain Driven Design Layered Architecture Account Invoice Modules * Entities Value Objects Aggregates * Repositories InvoiceLine Factories Services
  56. 56. From UML diagram to code
  57. 57. From UML diagram to code UML modeling MagicDraw
  58. 58. From UML diagram to code UML modeling XMI Export MagicDraw
  59. 59. From UML diagram to code + TigerMDSD UML modeling XMI Export configuration MagicDraw
  60. 60. From UML diagram to code + TigerMDSD Java/C# code UML modeling XMI Export configuration JPA configuration Database configuration Integration tests Test data generator WSDL XML Schema MagicDraw ...
  61. 61. From UML diagram to code + TigerMDSD Java/C# code UML modeling XMI Export configuration JPA configuration Database configuration Integration tests Test data generator WSDL MagicDraw MODEL is KING XML Schema ...
  62. 62. Requirement: Persist the domain model in a Relational database using JPA/Hibernate
  63. 63. TigerMDSD YAML configuration
  64. 64. # Setup TigerMDSD xmiModelPath: model.xml umlTool: EA basePackageName: dk.tigerteam.vetapp YAML configuration # Paths generateBaseClassesToPath: src/main/generated generateTestClassesToPath: src/test/generated generateResourcesToPath: src/main/generated-resources mapUmlPropertyTypes: - name: DateTime javaType: org.joda.time.DateTime hibernateCustomTypeMappingType: org.joda.time.contrib.hibernate.PersistentDateTime # JPA Setup jpaSetup: makeBaseClassesIntoMappedSuperClassesIfPossible: true generatePresentFieldInEmbeddables: true defaultToLazyFetchingForAllAssociations: true rootMappedSuperClass: dk.tigerteam.vetapp.model.AbstractEntity resolveTableNames: - defaultTableName: User resolvedTableName: _User # Extensions eventListeners: - dk.tigerteam.mdsd.java.generator.extension.BidirectionalGeneratorEventListener - dk.tigerteam.mdsd.jpa.hibernate.HibernateAssociationUnproxyListener - dk.tigerteam.mdsd.java.generator.extension.SerialVersionUIDGeneratorListener - dk.tigerteam.mdsd.java.generator.extension.PropertySugarMethodsEventListener - dk.tigerteam.mdsd.java.generator.extension.MetaTypeJavaDocListener - dk.tigerteam.mdsd.jpa.hibernate.HibernateDeleteOrphanListener - dk.tigerteam.mdsd.jpa.hibernate.HibernateValidatorNotNullListener - dk.tigerteam.mdsd.jpa.hibernate.HibernateFetchOptimizationListener
  65. 65. TigerMDSD - Java configuration
  66. 66. TigerMDSD - Java configuration 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);
  67. 67. Result: package dk.tigerteam.vetapp.model; @javax.persistence.Entity @javax.persistence.Table(name = "Customer") public class Customer extends dk.tigerteam.vetapp.model.AbstractEntity { private static final long serialVersionUID = 100497609L; @javax.persistence.Lob @javax.persistence.Basic @javax.persistence.Column(name = "comment") private String comment; @javax.persistence.OneToOne(cascade = { javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH }, fetch = javax.persistence.FetchType.LAZY) @javax.persistence.JoinColumn(name = "addressId") private dk.tigerteam.vetapp.model.Address address; @javax.persistence.Embedded @javax.persistence.AttributeOverrides( { @javax.persistence.AttributeOverride( name = "firstName", column = @javax.persistence.Column(nullable = false, name = "name_firstName")), @javax.persistence.AttributeOverride( name = "lastName", column = @javax.persistence.Column(nullable = false, name = "name_lastName")), @javax.persistence.AttributeOverride( name = "present", column = @javax.persistence.Column(nullable = false, name = "name_present")) }) private dk.tigerteam.vetapp.model.Name name; @javax.persistence.OneToMany(cascade = { javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH }, fetch = javax.persistence.FetchType.LAZY) @javax.persistence.JoinTable(name = "Customer_invoice", joinColumns = @javax.persistence.JoinColumn(name = "CustomerId"), inverseJoinColumns = @javax.persistence.JoinColumn(name = "invoiceId")) private java.util.Set<dk.tigerteam.vetapp.model.Invoice> invoiceCollection = new java.util.HashSet<dk.tigerteam.vetapp.model.Invoice>();
  68. 68. Automate the tedious details - under full control! Examples of configurable built in features:
  69. 69. Automate the tedious details - under full control! Examples of configurable built in features: Built-in Types JPA Field based persistence Bidirectional associations JPA Named Tables and Columns Property Sugar methods Hibernate Foreignkey Constraints Get Or New Property methods Hibernate Foreignkey Index Constructor (immuteable properties) Hibernate Fetch Optimization Class Hierarchy Java doc generator Hibernate Association Unproxying Serial Version UID generator Hibernate Table Comments MetaType Java doc generator Hibernate HH-3544 bug fix Serializable Pojo’s ToString/Equals/HashCode
  70. 70. TigerMDSD - Meta model level
  71. 71. TigerMDSD - Meta model level XmiReader reader = new EAXmiReader(); XmiReader reader = new MagicDrawXmiReader(); MetaModel metaModel = reader.read("model.xml");
  72. 72. TigerMDSD - Meta model level XmiReader reader = new EAXmiReader(); XmiReader reader = new MagicDrawXmiReader(); MetaModel metaModel = reader.read("model.xml");
  73. 73. TigerMDSD - Java Model
  74. 74. TigerMDSD - Java Model
  75. 75. TigerMDSD - Event based extension model
  76. 76. TigerMDSD - Event based extension model
  77. 77. TigerMDSD - Event Listeners
  78. 78. TigerMDSD - Event Listeners
  79. 79. TigerMDSD - Event Listeners public interface GeneratorEventListener { boolean handle(GeneratorEvent event); ... }
  80. 80. TigerMDSD - Example extension
  81. 81. TigerMDSD - Example extension @OneToMany @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) private Set<Tire> tires;
  82. 82. TigerMDSD - Example extension @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) { ... } }
  83. 83. TigerMDSD - Example extension
  84. 84. TigerMDSD - Example extension 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()) { // 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; } } } return true; } return false; }
  85. 85. “I thought it was all about rich domain models! What about my own domain code?”
  86. 86. “I thought it was all about rich domain models! What about my own domain code?” It is - but Usecase logic doesn’t belong in domain objects - so in reality very little code should enter the domain objects!
  87. 87. “I thought it was all about rich domain models! What about my own domain code?” It is - but Usecase logic doesn’t belong in domain objects - so in reality very little code should enter the domain objects! “Domain object design should focus on what the system is - NOT on what the system does!” James Coplien - DCI Talk at Öredev 2009
  88. 88. For the logic that belongs in the domain objects we have several choices... What we model:
  89. 89. For the logic that belongs in the domain objects we have several choices... What we model:
  90. 90. For the logic that belongs in the domain objects we have several choices... What we model: What we generate using Extension classes: (aka. 3 level inheritance)
  91. 91. For the logic that belongs in the domain objects we have several choices... What we model: What we generate al tion using Extension Op classes: (aka. 3 level inheritance)
  92. 92. For the logic that belongs in the domain objects we have several choices... What we model: What we generate al Alternatives: tion Generator Extensions using Extension Op Partial Classes classes: Mixins / Traits (aka. 3 level inheritance) Priviledged Aspects Protected Regions
  93. 93. Let’s get some UI running
  94. 94. UI framework considerations “Web 1.0” Client 5 Server HTML Page DOM over HttpResponse View 4 3 Model Parameters over HttpRequest Controller 2 DB 1
  95. 95. UI framework considerations “Client side RIA” Client 4 Server Requested data View to view as XML / JSON 5 DOM Model 3 1 Changes to model Controller encoded as parameters DB 2
  96. 96. UI framework considerations “Server driven Web 2.0 RIA” Client 8 Server 9 7 TerminalAdapter TerminalAdapter HTML Page over HttpResponse View 6 Automated by 5 DOM the RIA framework Model Parameters over HttpRequest Controller 1 4 3 DB 2
  97. 97. Component Architecture
  98. 98. Component Architecture “UI Component” • Button, Table, Tree, ... • Server-side data • Full Java API
  99. 99. Component Architecture HTTP(S) “UI Component” “Widget” • Button, Table, Tree, ... • Client-side peer for • Server-side data the component • Full Java API • Runs on JavaScript
  100. 100. Component Architecture HTTP(S) “UI Component” “Widget” • Button, Table, Tree, ... • Client-side peer for • Server-side data the component • Full Java API • Runs on JavaScript Java • Compiled with JDK
  101. 101. Component Architecture HTTP(S) “UI Component” “Widget” • Button, Table, Tree, ... • Client-side peer for • Server-side data the component • Full Java API • Runs on JavaScript Java Java • Compiled with JDK • Google Web Toolkit
  102. 102. Using TigerMDSD to create Vaadin Builders
  103. 103. Using TigerMDSD to create Vaadin Builders HorizontalLayout buttons = new HorizontalLayout(); buttons.setSpacing(true); Button discardChanges = new Button(text("general.button.back"), new Button.ClickListener() { public void buttonClick(Button.ClickEvent event) { getForm().discard(); getViewManager().showView(HomeView.class); } }); discardChanges.setStyleName(Button.STYLE_LINK); buttons.addComponent(discardChanges); // The save button Button apply = new Button(text("general.button.save"), new Button.ClickListener() { public void buttonClick(Button.ClickEvent event) { try { getForm().commit(); getPersistenceManager().merge(getFormEntity()); getViewManager().showView(HomeView.class); } catch (Exception e) { // Ignored, we'll let the Form handle the errors } } }); buttons.addComponent(apply); // Add the buttons to the layout getForm().getLayout().addComponent(buttons);
  104. 104. Using TigerMDSD to create Vaadin Builders HorizontalLayout buttons = new HorizontalLayout(); buttons.setSpacing(true); Button discardChanges = new Button(text("general.button.back"), formBuilder = new new Button.ClickListener() { HbnFormBuilder<User>(Application.getInstance().getContextUser()); public void buttonClick(Button.ClickEvent event) { formBuilder.getForm().getLayout().addComponent( getForm().discard(); horizontalLayout() getViewManager().showView(HomeView.class); .spacing(true) } .addComponents( }); button(text("general.button.back"), new Button.ClickListener() { discardChanges.setStyleName(Button.STYLE_LINK); public void buttonClick(Button.ClickEvent event) { buttons.addComponent(discardChanges); formBuilder.getForm().discard(); getViewManager().showView(HomeView.class); // The save button } Button apply = new Button(text("general.button.save"), new Button.ClickListener() { }), public void buttonClick(Button.ClickEvent event) { button(text("general.button.save"), new Button.ClickListener() { try { public void buttonClick(Button.ClickEvent event) { getForm().commit(); formBuilder.getForm().commit(); getPersistenceManager().merge(getFormEntity()); User user = formBuilder.getEntity(); getViewManager().showView(HomeView.class); getPersistenceManager().merge(user); } catch (Exception e) { getViewManager().showView(HomeView.class); // Ignored, we'll let the Form handle the errors } } } } ) }); ) buttons.addComponent(apply); // Add the buttons to the layout getForm().getLayout().addComponent(buttons);
  105. 105. Strong data binding Exchange fragile strings such as “customer.name.firstName” with typesafe code property().getName().getFirstName()
  106. 106. Strong data binding Exchange fragile strings such as “customer.name.firstName” with typesafe code property().getName().getFirstName() new SmartFormBuilder<Customer>(Customer, true) { protected void defineForm(SmartFormBuilder<Customer> formBuilder) { withProperties( property().getName().getFirstName(), property().getName().getLastName(), property().getAddress().getStreet(), property().getAddress().getZipCode(), property().getAddress().getCity(), property().getAddress().getCountry() ); } );
  107. 107. Web UI design patterns
  108. 108. Web UI design patterns M-V-C Model View Controller
  109. 109. Web UI design patterns M-V-C Model View Controller M-V-P Model View Presenter
  110. 110. Web UI design patterns M-V-C Model View Controller M-V-P Model View Presenter M-V-MV Model View ModelView/Presentation model
  111. 111. Web UI design patterns 1. Input/Event <<stateless>> 3 .Response Controller M-V-P 2 .Updates 4 .Pulls data View 5 .Change notification Model Model View Presenter Model View Controller M-V-MV Model View ModelView/Presentation model
  112. 112. Web UI design patterns 1. Execute Command 2 .Updates 1. Input/Event <<stateless>> <<stateless>> 3 .Response Controller 2 .Updates 3 .Updates Presenter 4 .Change Notification 4 .Pulls data View 5 .Change notification Model View Model Model View Controller Model View Presenter M-V-MV Model View ModelView/Presentation model
  113. 113. Web UI design patterns 1. Execute Command 2 .Updates 1. Input/Event <<stateless>> <<stateless>> 3 .Response Controller 2 .Updates 3 .Updates Presenter 4 .Change Notification 4 .Pulls data Pulls data View 5 .Change notification Model View Change notification Model Supervising controller Model View Controller Model View Presenter M-V-MV Model View ModelView/Presentation model
  114. 114. Web UI design patterns 1. Execute Command 2 .Updates 1. Input/Event <<stateless>> <<stateless>> 3 .Response Controller 2 .Updates 3 .Updates Presenter 4 .Change Notification 4 .Pulls data Pulls data View 5 .Change notification Model View Change notification Model Supervising controller Model View Controller Strong binding/Execute Command Apply/Commit Model View Presenter <<statefull>> Change ViewModel Errors notification ViewModel allows Invalid states View Model Model View ModelView/Presentation model
  115. 115. Thank you for your attention! For more information jeppe@tigerteam.dk Twitter: @jeppec

×