123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- ---
- 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
- <<../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
- <<../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[width=80%]
-
- 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.
-
-
-
- [[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[width=60%]
-
- <<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.
|