summaryrefslogtreecommitdiffstats
path: root/documentation/advanced/advanced-architecture.asciidoc
blob: 219443b4709bf0044488dfab4799b38f266b9aad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
---
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[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.

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[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.