summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-09 15:23:00 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-09 15:23:00 +0100
commita31a904dbdc068da723687702f7f26e99ac26489 (patch)
tree1ac5d08c13cdf0f0a7ef12fd3bdd8a32d2559746
parent42d3a9e04e72c77004abecc01a816ea1981e2fc5 (diff)
downloadkube-a31a904dbdc068da723687702f7f26e99ac26489.tar.gz
kube-a31a904dbdc068da723687702f7f26e99ac26489.zip
Updated the documentation to somewhat describe the new design.
-rw-r--r--docs/design.md258
1 files changed, 149 insertions, 109 deletions
diff --git a/docs/design.md b/docs/design.md
index e8a39915..38edde01 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -3,16 +3,17 @@
3## Overview 3## Overview
4Kube is supposed to be a small and concise codebase that is easy to modify and evolve. 4Kube is supposed to be a small and concise codebase that is easy to modify and evolve.
5 5
6It's following a reactive model, where in one direction we have controllers generating modifications, and in the other direction models updating themselves on changes. 6It's following a reactive model, where in one direction we have actions generating modifications, and in the other direction models updating themselves on changes.
7 7
8The overall architecture is split into three layers; Ui, Domain Logic and Infrastructure. 8The overall architecture is split into three layers; Ui, Domain Logic and Infrastructure.
9 9
10``` 10```
11+----------------------------+ 11+----------------------------+
12| UI / Application | 12| UI Components |
13+----------------------------+ 13+----------------------------+
14| | 14| |
15| Domain Logic | 15| Domain Logic |
16| Actions/Models |
16| | 17| |
17+--------------+------+------+ 18+--------------+------+------+
18| | | | 19| | | |
@@ -21,9 +22,9 @@ The overall architecture is split into three layers; Ui, Domain Logic and Infras
21+--------------+------+------+ 22+--------------+------+------+
22``` 23```
23 24
24The UI Layer consists of views (mostly written in QML), view-models (models that are view specific and potentially implement user interaction details), and the glue code to use various controllers from the interface. Different UI layers may exist for different form factors. 25The UI Layer consists of views (mostly written in QML), view-models (models that are view specific and potentially implement user interaction details), and the glue code to use various models and actions from the interface. Different UI layers may exist for different form factors.
25 26
26The domain logic layer holds the application state. It povides models to access data and controllers to act upon it. The domain logic is by definition Kube specific and not sharable with other applications, as it needs to be taylored exactly according to the requirements of Kube. 27The domain logic layer holds the application state. It povides models to access data and actions to act upon it. The domain logic is by definition Kube specific and not sharable with other applications, as it needs to be taylored exactly according to the requirements of Kube.
27 28
28The infrastructure layer provides: 29The infrastructure layer provides:
29 30
@@ -32,20 +33,38 @@ The infrastructure layer provides:
32* Various functionality provided by libraries (email sending, ldap, iTip handling, iCal implementation (kcalcore), vCard implementation, ...) 33* Various functionality provided by libraries (email sending, ldap, iTip handling, iCal implementation (kcalcore), vCard implementation, ...)
33Various bits of the infrastructure layer may be exchanged on different platforms, to i.e. integrate into native infrastructure providers on a platform. 34Various bits of the infrastructure layer may be exchanged on different platforms, to i.e. integrate into native infrastructure providers on a platform.
34 35
35Note: By using the onion architecture we ensure the infrastructure is exchangable just as well as the UI.
36Note: The domain objects might also be specified in Akonadi Next.
37
38## UI / Application 36## UI / Application
39The UI / Application layer contains all the views, and their composition, that make up the application. 37The UI / Application layer contains all the view components, and their composition, that make up the application.
40All the interactions between the different components are defined here. 38All the interactions between the different components are defined here.
41 39
42### UI Component Interaction 40## Components
43UI components will have to be able to: 41The application consists of various application components. A component could be a maillist, an event-editor or the complete kube-mail application. Each component is instantiable on it's own, and has an API to interact with it. The API i.e. allows to set a folder for the maillist, or an event for the event-editor. Components can be nested (a component can instantiate another component)
42
43A component primarily is a QML UI.
44
45The QML UI is built on top of:
46
47* One or more models that are instantiated to provide the data.
48* Actions that are instantiated in QML.
49
50## Component interaction
51The application is made up of various nested components that often need to interact with each other.
52
53If we look at the example of the org.kube.mail component:
541. The folderlist-component current-folder property is connected to maillist parentFolder property to display the mails of the currently selected folder.
552. The "add-note" action might either switch to the org.kube.note application as currently displayed component, or it might just display a quick-note widget directly inline.
56
57The first usecase can be achieved by the parent component doing a property binding to connect the different components together as desired.
58
59The second usecase requires actions to interact with 'a' parent component, but without knowing with which one. Actions can thus be handled by ActionHandlers anywhere in the application.
44 60
45* Load certain views directly inline (e.g. load a calendar into a mail with an invitation, or a maileditor into the maillist) 61This makes it possible for i.e. a maillist to display a note-widget directly inline, or letting the parent component handle the action to show a full note editor.
46* Change the overall application state by i.e. triggering the calendar to show a certain date-range. 62If nothing handles the action, the root component (the shell)can switch to the note application component.
47 63
48This is ideally decoupled in a way that the provider of such a service can be replaced without changing the users of the service. Additionally it should be possible for external applications (such as a plasmoid), to load the same views, possibly through the same mechanism. 64## Third party users of components
65Since components are self contained and made available throuh the KPackage sytem, external applications can load fully functional Kube components.
66
67For example, the KDE calendar plasmoid could load the Kube Event Viewer component when available, and thus provide Kube's full functionality of that component, including all actions etc, without having to reimplement the Domain Logic (as is the case if only data access is provided through akonadi).
49 68
50## Domain Logic 69## Domain Logic
51 70
@@ -57,55 +76,58 @@ Models should always be reactive and configured with a query, so they are asynch
57 76
58By implementing everything according to that model we can later on achieve lazy-loading of properties and zero-copy (or at least close to zero-copy) directly from storage without modifying anything besides data access. 77By implementing everything according to that model we can later on achieve lazy-loading of properties and zero-copy (or at least close to zero-copy) directly from storage without modifying anything besides data access.
59 78
60### Controllers 79Models are self contained and have an API to set i.e. a query for what to load. Models can load data from anywhere. Typically models are implemented in C++ to interface with the rest of the system, but some models may also be implemented directly in QML.
61Use-case specific controllers are used to operate on the data. Controllers allow to completely separate the modifications from the view.
62Rather than splitting controllers by domain type (e.g. an email controller, or a calendar controller), we specifically write controllers for specific usecases (e.g. an email editor), that exposes all required actions. That way we ensure that the API's a UI is working with is always clear an consice, and that we have all domain logic captured in the domain logic layer, rather than the UI layer.
63Of course controllers will need to share functionality internally as soon as an action is available from more than one place.
64 80
65### Email Domain Logic 81### Actions
66* Folder list 82An action represents something that can be done, such as "mark as read", "delete", "move somewhere", but also "show this mail" or "give me a composer to write a mail".
67 * Folder List Controller
68 * Move mail to folder
69 * Move/Copy/Delete folder
70 * Synchronize folder
71 * Folder List Model
72 * Mixes akonadi next queries and subqueries (folder list with smart folders)
73 * name
74 * statistics
75 83
76* Mail list 84An action has:
77 * MailListController 85* an id (i.e. org.kube.actions.make-as-read)
78 * Mark as read 86* an active state (a property for the UI to know when the action can be triggered, that changes depending on the context)
79 * Flag as important 87* an action context, which contains everything the action needs to execute.
80 * Move to trash 88* an icon
81 * MailListModel 89* a name
82 * subject
83 * date
84 * sender
85 * folder
86 * ThreadModel
87 * thread leader (otherwise like maillist model)
88 * number of mails in thread
89 90
90* Mail Viewer 91The action context contains the dataset the action works upon plus any additional information that is required. A mark-as-read action for instance only requires the mail-set to work on, while a tag-with action requires any entity (mail, event, ...) and a tag (unless there is one action per tag...).
91 * MailViewController 92The action can, through property-binding, reevaluate its active state based on the currently set context that the UI continuously updates through property binding. Context objects can be shared by various actions.
92 * reply
93 * forward
94 * move to trash
95 * MailModel
96 * subject, date, sender, folder, content, attachments
97 93
98## Infrastructure 94#### Automatic action discovery
95While in many places explicit instantiation of actions is desirable, sometimes we may want to offer all available actions for a certain type. For this it should be possible to i.e. query for all actions that apply to a mail. That way it is possible to centrally add a new action that automatically becomes available everywhere. Note that this only works for actions that don't require an additional UI, since the components would have to embedd that somewhere.
96
97#### Implementation
98Actions are objects that provide the API, and that QML can instantiate directly with it's id. The C++ implementation looks up the action handler via a broker.
99
100* Action: The interface to execute/launch the action. Forwards request and context to broker.
101* ActionHandler: A handler for a specific action. Registers itself with the broker.
102* ActionBroker: Forwards action requests to handlers.
103* Context: The context containing everything the handler needs to execute the action.
104
105### Controller
106For every domain object a controller is implemented to be able to edit the domain object. The domain object is a QObject with a QObject-property for every property of th edomain object and a QValidator, so editors can easily be build using property binding while providing property-level validation and feedback.
107
108The domain object is exposed as an opaque QVariant that can i.e. be used in an action-context. This way details from the infrastructure layer don't leak to the UI layer
99 109
110### Notifications
111The system will provide notifications from various sources.
112
113Notifications could be:
114
115* New mails arrived
116* An error occurred
117* A synchronization is in progress
118* ...
119
120Notifications can be displayed in various places of the application.
121
122## Infrastructure
100The infrastructure layer interfaces with the rest of the system. It is the place where we can integrate with various native infrastructure parts. 123The infrastructure layer interfaces with the rest of the system. It is the place where we can integrate with various native infrastructure parts.
101The interface of the infrastructure layer, that is used by the domain logic, may not expose any implementation details of any infrastructure part, to ensure that all infrastructure parts are exchangable. 124The interface of the infrastructure layer, that is used by the domain logic, may not expose any implementation details of any infrastructure part, to ensure that all infrastructure parts are exchangable.
102 125
103Note: The infrastructure blocks will use only types provided by the domain logic. This means i.e. that no KCalCore containers may be used in such an interface. To avoid hard dependencies on any specific implementation, the infrastructure parts will have to have interfaces, and a factory must be used to supply concrete implementations. That way it is also possible to inject dummy implementations.
104
105### Akonadi Next 126### Akonadi Next
106Akonadi Next is used for primary data access and handles all synchronization. 127Akonadi Next is used for primary data access and handles all synchronization.
107 128
108Interactions with Akonadi Next involve: 129Interactions with Akonadi Next involve:
130
109* Adding/removing/configuring resources 131* Adding/removing/configuring resources
110* Triggering synchronization 132* Triggering synchronization
111* Querying of data 133* Querying of data
@@ -141,71 +163,89 @@ Probably requires access to identities in some way.
141* ObjectTreeParser 163* ObjectTreeParser
142* KMime 164* KMime
143 165
144## Interaction with external applications
145External applications, like the KDE calendar plasmoid, should be able to load parts of Kube when available. It should for instance be possible to load the Event editor as embeddable QML component, that is fully functional. That way it becomes very easy for third parties to provide extra functionality if Kube is installed, without having to reimplement the Domain Logic (as is the case if only data access is provided through akonadi).
146
147The same mechanism should probably be used by Kube itself to ensure loose coupling and allow mashups with various content types.
148
149Note: We'll probably want a component-viewer application to easily load and test individual components (similar to plasmoidviewer).
150
151## Testing 166## Testing
152 167
153* Controllers can be tested by providing mock implementations of the relevant infrastructure parts. 168TBD
154* Models can be tested by providing fake implementations of the relevant infrastructure parts.
155* Infrastructure parts can be tested individually.
156
157# The new design
158
159The application consists of various application components. A component could be a maillist, an event-editor or the complete kube-mail application. Each component is instantiable on it's own, and has an API to interact with it. The API i.e. allows to set a folder for the maillist, or an event for the event-editor. Components can be nested (a component can instantiate another component)
160
161Components are requested from a factory, allowing to return different versions depending on modifiers passed in, or the global application context (i.e. mobile variants of the UI).
162
163A component primarily is a QML UI.
164The QML UI is built on top of:
165* One or more models that are instantiated to provide the data.
166* Actions that are instatiated in QML.
167
168## Models
169 169
170Models are self contained and have an API to set i.e. a query for what to load. Models can load data from anywhere. 170## Problems/Notes:
171* Dynamic switching between various component UI's can be solved using KPackage
172* change-requests sometimes also need to be handled by sub-components
173** If you hit the reply button in the main view, but replies always work inline in the messagelist, the messagelist should still be able to capture the request and handle it. But if the request only goes to parent objects, that is not possible. Perhaps we need a pub-sub mechanism.
171 174
175## Example usage in QML
172 176
173## Actions 177```
174 178KubeActions.Action {
175An action represents something that can be done, such as "mark as read", "delete", "move somewhere". 179 requestId: "org.kde.kube.mail.reply"
176 180 onRequest {
177An action has: 181 mail: context.mail
178* an id (org.kube.mail.make-as-read) 182 }
179* an active state (a property for the UI to know when the action can be triggered, that changes depending on the context) 183}
180* an action context, which contains everything the action needs to execute. 184
181* an action icon 185KubeActions.ActionContext {
182* an action name 186 id: actionContext
183 187 mail: kubeMailListView.currentMail
184The action context contains the dataset the action works upon plus any additional information that is required. A mark-as-read action for instance only requires the mail-set to work on, while a tag-with action requires any entity (mail, event, ...) and a tag (unless there is one action per tag...). 188}
185The action can through property-binding reevaluate its active state based on the currently set context that the UI continuously updates through property binding. Context objects can be shared by various actions. 189
186 190KubeActions.ActionHandle {
187### Automatic action discovery 191 property int progress
188While in may places explicit instantiation of actions is desirable, sometimes we may want to offer all available actions for a certain type. For this it should be possible to i.e. query for all actions that apply to a mail. That way it is possible to centrally add a new action that automatically becomes available everywhere. Note that this only works for actions that don't require an additional UI, since the components would have to embedd that somewhere. 192 property bool complete
189 193 property bool error
190### Implementation 194 property string errormessage
191 195}
192Actions could be simply objects that provide the API, and that QML can instantiate directly with it's id. The C++ implementation of the action can then lookup the action implementation using the id and call it with it's context when executed. 196
193 197KubeActions.Action {
194## Component interaction 198 id: markAsReadAction
195 199 action: "org.kde.kube.action.mark-as-read"
196The application is made up of various nested components that sometimes need to interact with each other. 200 context: actionContext
197 201 //execute() returns an ActionHandle
198If we look at the example of the org.kube.mail component: 202}
1991. The folderlist-component current-folder property is connected to maillist parentFolder property to display the mails of the currently selected folder. 203
2002. The "add-note" action might either switch the application state to the org.kube.note application, or it might just display a quick-note widget directly inline. 204KubeComponents.FolderList {
201 205 id: kubeFolderListView
202The first usecase can be achieved by the parent component doing a property binding to connect the different components together as desired. 206}
207
208KubeComponents.MailList {
209 id: kubeMailListView
210 parentFolder: kubeFolderListView.currentFolder
211}
212
213KubeComponents.MailView {
214 id: kubeMailView
215 mail: kubeMailListView.currentMail
216}
217```
203 218
204The second usecase requires actions to interact with 'a' parent component, but without knowing with which one. Actions can thus post requests for application state changes, that bubble up through the components and can be catched by any of the parent components. 219## Email Domain Logic
220* Folder list
221 * Folder List Controller
222 * Move mail to folder
223 * Move/Copy/Delete folder
224 * Synchronize folder
225 * Folder List Model
226 * Mixes akonadi next queries and subqueries (folder list with smart folders)
227 * name
228 * statistics
205 229
206This makes it possible for i.e. a maillist to display a note-widget directly inline, or handing the request up to the parent component which could show a full note editor. 230* Mail list
207And if nothing handles the request it bubbles up to the root component (the shell), which can then switch to the note application component. 231 * MailListController
232 * Mark as read
233 * Flag as important
234 * Move to trash
235 * MailListModel
236 * subject
237 * date
238 * sender
239 * folder
240 * ThreadModel
241 * thread leader (otherwise like maillist model)
242 * number of mails in thread
208 243
209### Application state changes requests 244* Mail Viewer
245 * MailViewController
246 * reply
247 * forward
248 * move to trash
249 * MailModel
250 * subject, date, sender, folder, content, attachments
210 251
211A request always requires a context that tells the handler what to do. This could be the note to edit, or the date-range to display in the calendar, or the recepient for the mail composer.