You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

advanced-architecture.asciidoc 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. ---
  2. title: Advanced Application Architectures
  3. order: 10
  4. layout: page
  5. ---
  6. [[advanced.architecture]]
  7. = Advanced Application Architectures
  8. In this section, we continue from the basic application architectures described
  9. in
  10. <<dummy/../../../framework/application/application-architecture#application.architecture,"Building
  11. the UI">> and discuss some of the more advanced patterns that are often used in
  12. Vaadin applications.
  13. [[advanced.architecture.layering]]
  14. == Layered Architectures
  15. Layered architectures, where each layer has a clearly distinct responsibility,
  16. are probably the most common architectures. Typically, applications follow at
  17. least a three-layer architecture:
  18. * User interface (or presentation) layer
  19. * Domain layer
  20. * Data store layer
  21. Such an architecture starts from a __domain model__, which defines the data
  22. model and the "business logic" of the application, typically as beans or POJOs.
  23. A user interface is built on top of the domain model, in our context with the
  24. Vaadin Framework. The Vaadin user interface could be bound directly to the data
  25. model through the Vaadin Data Model, described in
  26. <<dummy/../../../framework/datamodel/datamodel-overview.asciidoc#datamodel.overview,"Binding
  27. Components to Data">>. Beneath the domain model lies a data store, such as a
  28. relational database. The dependencies between the layers are restricted so that
  29. a higher layer may depend on a lower one, but never the other way around.
  30. [[figure.advanced.architecture.layering]]
  31. .Three-Layer Architecture
  32. image::img/three-layer-architecture-hi.png[]
  33. An __application layer__ (or __service layer__) is often distinguished from the
  34. domain layer, offering the domain logic as a service, which can be used by the
  35. user interface layer, as well as for other uses. In Java EE development,
  36. Enterprise JavaBeans (EJBs) are typically used for building this layer.
  37. An __infrastructure layer__ (or __data access layer__) is often distinguished
  38. from the data store layer, with a purpose to abstract the data store. For
  39. example, it could involve a persistence solution such as JPA and an EJB
  40. container. This layer becomes relevant with Vaadin when binding Vaadin
  41. components to data with the JPAContainer, as described in
  42. <<dummy/../../../framework/jpacontainer/jpacontainer-overview.asciidoc#jpacontainer.overview,"Vaadin
  43. JPAContainer">>.
  44. [[advanced.architecture.mvp]]
  45. == Model-View-Presenter Pattern
  46. The Model-View-Presenter (MVP) pattern is one of the most common patterns in
  47. developing large applications with Vaadin. It is similar to the older
  48. Model-View-Controller (MVC) pattern, which is not as meaningful in Vaadin
  49. development. Instead of an implementation-aware controller, there is an
  50. implementation-agnostic presenter that operates the view through an interface.
  51. The view does not interact directly with the model. This isolates the view
  52. implementation better than in MVC and allows easier unit testing of the
  53. presenter and model.
  54. [[figure.advanced.architecture.mvp]]
  55. .Model-View-Presenter Pattern
  56. image::img/mvp-pattern-hi.png[]
  57. <<figure.advanced.architecture.mvp>> illustrates the MVP pattern with a simple
  58. calculator. The domain model is realized in the [classname]#Calculator# class,
  59. which includes a data model and some model logic operations. The
  60. [classname]#CalculatorViewImpl# is a Vaadin implementation of the view, defined
  61. in the [interfacename]#CalculatorView# interface. The
  62. [classname]#CalculatorPresenter# handles the user interface logic. User
  63. interaction events received in the view are translated into
  64. implementation-independent events for the presenter to handle (the view
  65. implementation could also just call the presenter).
  66. Let us first look how the model and view are bound together by the presenter in
  67. the following example:
  68. [source, java]
  69. ----
  70. // Create the model and the Vaadin view implementation
  71. CalculatorModel model = new CalculatorModel();
  72. CalculatorViewImpl view = new CalculatorViewImpl();
  73. // The presenter binds the model and view together
  74. new CalculatorPresenter(model, view);
  75. // The view implementation is a Vaadin component
  76. layout.addComponent(view);
  77. ----
  78. You could add the view anywhere in a Vaadin application, as it is a composite
  79. component.
  80. [[advanced.architecture.mvp.model]]
  81. === The Model
  82. Our business model is quite simple, with one value and a number of operations
  83. for manipulating it.
  84. [source, java]
  85. ----
  86. /** The model **/
  87. class CalculatorModel {
  88. private double value = 0.0;
  89. public void clear() {
  90. value = 0.0;
  91. }
  92. public void add(double arg) {
  93. value += arg;
  94. }
  95. public void multiply(double arg) {
  96. value *= arg;
  97. }
  98. public void divide(double arg) {
  99. if (arg != 0.0)
  100. value /= arg;
  101. }
  102. public double getValue() {
  103. return value;
  104. }
  105. public void setValue(double value) {
  106. this.value = value;
  107. }
  108. }
  109. ----
  110. [[advanced.architecture.mvp.view]]
  111. === The View
  112. The purpose of the view in MVP is to display data and receive user interaction.
  113. It relays the user interaction to the presenter in an fashion that is
  114. independent of the view implementation, that is, no Vaadin events. It is defined
  115. as a UI framework interface that can have multiple implementations.
  116. [source, java]
  117. ----
  118. interface CalculatorView {
  119. public void setDisplay(double value);
  120. interface CalculatorViewListener {
  121. void buttonClick(char operation);
  122. }
  123. public void addListener(CalculatorViewListener listener);
  124. }
  125. ----
  126. The are design alternatives for the view. It could receive the listener in its
  127. constructor, or it could just know the presenter. Here, we forward button clicks
  128. as an implementation-independent event.
  129. As we are using Vaadin, we make a Vaadin implementation of the interface as
  130. follows:
  131. [source, java]
  132. ----
  133. class CalculatorViewImpl extends CustomComponent
  134. implements CalculatorView, ClickListener {
  135. private Label display = new Label("0.0");
  136. public CalculatorViewImpl() {
  137. GridLayout layout = new GridLayout(4, 5);
  138. // Create a result label that spans over all
  139. // the 4 columns in the first row
  140. layout.addComponent(display, 0, 0, 3, 0);
  141. // The operations for the calculator in the order
  142. // they appear on the screen (left to right, top
  143. // to bottom)
  144. String[] operations = new String[] {
  145. "7", "8", "9", "/", "4", "5", "6",
  146. "*", "1", "2", "3", "-", "0", "=", "C", "+" };
  147. // Add buttons and have them send click events
  148. // to this class
  149. for (String caption: operations)
  150. layout.addComponent(new Button(caption, this));
  151. setCompositionRoot(layout);
  152. }
  153. public void setDisplay(double value) {
  154. display.setValue(Double.toString(value));
  155. }
  156. /* Only the presenter registers one listener... */
  157. List<CalculatorViewListener> listeners =
  158. new ArrayList<CalculatorViewListener>();
  159. public void addListener(CalculatorViewListener listener) {
  160. listeners.add(listener);
  161. }
  162. /** Relay button clicks to the presenter with an
  163. * implementation-independent event */
  164. @Override
  165. public void buttonClick(ClickEvent event) {
  166. for (CalculatorViewListener listener: listeners)
  167. listener.buttonClick(event.getButton()
  168. .getCaption().charAt(0));
  169. }
  170. }
  171. ----
  172. [[advanced.architecture.mvp.presenter]]
  173. === The Presenter
  174. The presenter in MVP is a middle-man that handles all user interaction logic,
  175. but in an implementation-independent way, so that it doesn't actually know
  176. anything about Vaadin. It shows data in the view and receives user interaction
  177. back from it.
  178. [source, java]
  179. ----
  180. class CalculatorPresenter
  181. implements CalculatorView.CalculatorViewListener {
  182. CalculatorModel model;
  183. CalculatorView view;
  184. private double current = 0.0;
  185. private char lastOperationRequested = 'C';
  186. public CalculatorPresenter(CalculatorModel model,
  187. CalculatorView view) {
  188. this.model = model;
  189. this.view = view;
  190. view.setDisplay(current);
  191. view.addListener(this);
  192. }
  193. @Override
  194. public void buttonClick(char operation) {
  195. // Handle digit input
  196. if ('0' <= operation && operation <= '9') {
  197. current = current * 10
  198. + Double.parseDouble("" + operation);
  199. view.setDisplay(current);
  200. return;
  201. }
  202. // Execute the previously input operation
  203. switch (lastOperationRequested) {
  204. case '+':
  205. model.add(current);
  206. break;
  207. case '-':
  208. model.add(-current);
  209. break;
  210. case '/':
  211. model.divide(current);
  212. break;
  213. case '*':
  214. model.multiply(current);
  215. break;
  216. case 'C':
  217. model.setValue(current);
  218. break;
  219. } // '=' is implicit
  220. lastOperationRequested = operation;
  221. current = 0.0;
  222. if (operation == 'C')
  223. model.clear();
  224. view.setDisplay(model.getValue());
  225. }
  226. }
  227. ----
  228. In the above example, we held some state information in the presenter.
  229. Alternatively, we could have had an intermediate controller between the
  230. presenter and the model to handle the low-level button logic.