diff options
author | Markus Koivisto <markus@vaadin.com> | 2016-01-22 14:55:18 +0200 |
---|---|---|
committer | Markus Koivisto <markus@vaadin.com> | 2016-01-22 14:55:18 +0200 |
commit | 99d6de546c74f0eed230ea8253dda6b85109d2e7 (patch) | |
tree | 10fc21c557566fe3241e6e13499df18d80f8dcb2 /documentation/advanced/advanced-architecture.asciidoc | |
parent | 610736d9f373d4b37fd39ff8f90aabd13eab7926 (diff) | |
download | vaadin-framework-99d6de546c74f0eed230ea8253dda6b85109d2e7.tar.gz vaadin-framework-99d6de546c74f0eed230ea8253dda6b85109d2e7.zip |
Add documentation to master branch
Change-Id: I2504bb10f1ae73ec0cbc08b7ba5a88925caa1674
Diffstat (limited to 'documentation/advanced/advanced-architecture.asciidoc')
-rw-r--r-- | documentation/advanced/advanced-architecture.asciidoc | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/documentation/advanced/advanced-architecture.asciidoc b/documentation/advanced/advanced-architecture.asciidoc new file mode 100644 index 0000000000..437c6f4a1d --- /dev/null +++ b/documentation/advanced/advanced-architecture.asciidoc @@ -0,0 +1,298 @@ +--- +title: Advanced Application Architectures +order: 10 +layout: page +--- + +[[advanced.architecture]] += Advanced Application Architectures + +In this section, we continue from the basic application architectures described +in +<<dummy/../../../framework/application/application-architecture#application.architecture,"Building +the UI">> and discuss some of the more advanced patterns that are often used in +Vaadin applications. + +[[advanced.architecture.layering]] +== Layered Architectures + +Layered architectures, where each layer has a clearly distinct responsibility, +are probably the most common architectures. Typically, applications follow at +least a three-layer architecture: + +* User interface (or presentation) layer +* Domain layer +* Data store layer + +Such an architecture starts from a __domain model__, which defines the data +model and the "business logic" of the application, typically as beans or POJOs. +A user interface is built on top of the domain model, in our context with the +Vaadin Framework. The Vaadin user interface could be bound directly to the data +model through the Vaadin Data Model, described in +<<dummy/../../../framework/datamodel/datamodel-overview.asciidoc#datamodel.overview,"Binding +Components to Data">>. Beneath the domain model lies a data store, such as a +relational database. The dependencies between the layers are restricted so that +a higher layer may depend on a lower one, but never the other way around. + +[[figure.advanced.architecture.layering]] +.Three-Layer Architecture +image::img/three-layer-architecture-hi.png[] + +An __application layer__ (or __service layer__) is often distinguished from the +domain layer, offering the domain logic as a service, which can be used by the +user interface layer, as well as for other uses. In Java EE development, +Enterprise JavaBeans (EJBs) are typically used for building this layer. + +An __infrastructure layer__ (or __data access layer__) is often distinguished +from the data store layer, with a purpose to abstract the data store. For +example, it could involve a persistence solution such as JPA and an EJB +container. This layer becomes relevant with Vaadin when binding Vaadin +components to data with the JPAContainer, as described in +<<dummy/../../../framework/jpacontainer/jpacontainer-overview.asciidoc#jpacontainer.overview,"Vaadin +JPAContainer">>. + + +[[advanced.architecture.mvp]] +== Model-View-Presenter Pattern + +The Model-View-Presenter (MVP) pattern is one of the most common patterns in +developing large applications with Vaadin. It is similar to the older +Model-View-Controller (MVC) pattern, which is not as meaningful in Vaadin +development. Instead of an implementation-aware controller, there is an +implementation-agnostic presenter that operates the view through an interface. +The view does not interact directly with the model. This isolates the view +implementation better than in MVC and allows easier unit testing of the +presenter and model. + +[[figure.advanced.architecture.mvp]] +.Model-View-Presenter Pattern +image::img/mvp-pattern-hi.png[] + +<<figure.advanced.architecture.mvp>> illustrates the MVP pattern with a simple +calculator. The domain model is realized in the [classname]#Calculator# class, +which includes a data model and some model logic operations. The +[classname]#CalculatorViewImpl# is a Vaadin implementation of the view, defined +in the [interfacename]#CalculatorView# interface. The +[classname]#CalculatorPresenter# handles the user interface logic. User +interaction events received in the view are translated into +implementation-independent events for the presenter to handle (the view +implementation could also just call the presenter). + +Let us first look how the model and view are bound together by the presenter in +the following example: + + +[source, java] +---- + +// Create the model and the Vaadin view implementation +CalculatorModel model = new CalculatorModel(); +CalculatorViewImpl view = new CalculatorViewImpl(); + +// The presenter binds the model and view together +new CalculatorPresenter(model, view); + +// The view implementation is a Vaadin component +layout.addComponent(view); +---- + +You could add the view anywhere in a Vaadin application, as it is a composite +component. + +[[advanced.architecture.mvp.model]] +=== The Model + +Our business model is quite simple, with one value and a number of operations +for manipulating it. + + +[source, java] +---- +/** The model **/ +class CalculatorModel { + private double value = 0.0; + + public void clear() { + value = 0.0; + } + + public void add(double arg) { + value += arg; + } + + public void multiply(double arg) { + value *= arg; + } + + public void divide(double arg) { + if (arg != 0.0) + value /= arg; + } + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } +} +---- + + +[[advanced.architecture.mvp.view]] +=== The View + +The purpose of the view in MVP is to display data and receive user interaction. +It relays the user interaction to the presenter in an fashion that is +independent of the view implementation, that is, no Vaadin events. It is defined +as a UI framework interface that can have multiple implementations. + + +[source, java] +---- +interface CalculatorView { + public void setDisplay(double value); + + interface CalculatorViewListener { + void buttonClick(char operation); + } + public void addListener(CalculatorViewListener listener); +} +---- + +The are design alternatives for the view. It could receive the listener in its +constructor, or it could just know the presenter. Here, we forward button clicks +as an implementation-independent event. + +As we are using Vaadin, we make a Vaadin implementation of the interface as +follows: + + +[source, java] +---- +class CalculatorViewImpl extends CustomComponent + implements CalculatorView, ClickListener { + private Label display = new Label("0.0"); + + public CalculatorViewImpl() { + GridLayout layout = new GridLayout(4, 5); + + // Create a result label that spans over all + // the 4 columns in the first row + layout.addComponent(display, 0, 0, 3, 0); + + // The operations for the calculator in the order + // they appear on the screen (left to right, top + // to bottom) + String[] operations = new String[] { + "7", "8", "9", "/", "4", "5", "6", + "*", "1", "2", "3", "-", "0", "=", "C", "+" }; + + // Add buttons and have them send click events + // to this class + for (String caption: operations) + layout.addComponent(new Button(caption, this)); + + setCompositionRoot(layout); + } + + public void setDisplay(double value) { + display.setValue(Double.toString(value)); + } + + /* Only the presenter registers one listener... */ + List<CalculatorViewListener> listeners = + new ArrayList<CalculatorViewListener>(); + + public void addListener(CalculatorViewListener listener) { + listeners.add(listener); + } + + /** Relay button clicks to the presenter with an + * implementation-independent event */ + @Override + public void buttonClick(ClickEvent event) { + for (CalculatorViewListener listener: listeners) + listener.buttonClick(event.getButton() + .getCaption().charAt(0)); + } +} +---- + + +[[advanced.architecture.mvp.presenter]] +=== The Presenter + +The presenter in MVP is a middle-man that handles all user interaction logic, +but in an implementation-independent way, so that it doesn't actually know +anything about Vaadin. It shows data in the view and receives user interaction +back from it. + + +[source, java] +---- +class CalculatorPresenter + implements CalculatorView.CalculatorViewListener { + CalculatorModel model; + CalculatorView view; + + private double current = 0.0; + private char lastOperationRequested = 'C'; + + public CalculatorPresenter(CalculatorModel model, + CalculatorView view) { + this.model = model; + this.view = view; + + view.setDisplay(current); + view.addListener(this); + } + + @Override + public void buttonClick(char operation) { + // Handle digit input + if ('0' <= operation && operation <= '9') { + current = current * 10 + + Double.parseDouble("" + operation); + view.setDisplay(current); + return; + } + + // Execute the previously input operation + switch (lastOperationRequested) { + case '+': + model.add(current); + break; + case '-': + model.add(-current); + break; + case '/': + model.divide(current); + break; + case '*': + model.multiply(current); + break; + case 'C': + model.setValue(current); + break; + } // '=' is implicit + + lastOperationRequested = operation; + + current = 0.0; + if (operation == 'C') + model.clear(); + view.setDisplay(model.getValue()); + } +} +---- + +In the above example, we held some state information in the presenter. +Alternatively, we could have had an intermediate controller between the +presenter and the model to handle the low-level button logic. + + + + + |