forked from Sefford/kor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
198 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,203 @@ | ||
kor | ||
Kor | ||
=== | ||
|
||
A Clean Architecture Implementation core library. | ||
|
||
Download | ||
-------- | ||
|
||
### Bundle | ||
|
||
Kor-Common comes bundled in `jar` format. Grab the latest bundle from [here](hhttp://search.maven.org/remotecontent?filepath=com/sefford/kor-common/1.31/kor-common-1.31.jar) | ||
|
||
Kor-Android comes bundled in `aar` format. Grab the latest bundle from [here](http://search.maven.org/remotecontent?filepath=com/sefford/kor-android/1.0.1/kor-android-1.0.1.aar) | ||
|
||
Kor-Retrofit comes bundled in `aar` format. Grab the latest bundle from [here](http://search.maven.org/remotecontent?filepath=com/sefford/kor-retrofit/1.0.10/kor-retrofit-1.0.10.aar) | ||
|
||
### Maven | ||
#### Kor-Common | ||
```XML | ||
<dependency> | ||
<groupId>com.sefford</groupId> | ||
<artifactId>kor-common</artifactId> | ||
<version>1.31</version> | ||
</dependency> | ||
``` | ||
#### Kor-Android | ||
```XML | ||
<dependency> | ||
<groupId>com.sefford</groupId> | ||
<artifactId>kor-android</artifactId> | ||
<version>1.0.1</version> | ||
<type>aar</type> | ||
</dependency> | ||
``` | ||
#### Kor-Retrofit | ||
```XML | ||
<dependency> | ||
<groupId>com.sefford</groupId> | ||
<artifactId>kor-retrofit</artifactId> | ||
<version>1.0.10</version> | ||
<type>aar</type> | ||
</dependency> | ||
``` | ||
|
||
### Gradle | ||
#### Kor-Common | ||
```groovy | ||
compile 'com.sefford:kor-common:1.31' | ||
``` | ||
#### Kor-Android | ||
```groovy | ||
compile 'com.sefford:kor-android:1.0.1@aar' | ||
``` | ||
#### Kor-Retrofit | ||
```groovy | ||
compile 'com.sefford:kor-retrofit:1.0.10@aar' | ||
``` | ||
|
||
What is Kor? | ||
============ | ||
|
||
Kor is the skeleton of a clean-ish architecture implementation. There are many ways of implementing a clean architecture: | ||
|
||
* Ports and adapters | ||
* Hexagonal Architecture | ||
* VIPER Architecture | ||
|
||
The aim of Kor is to provide a fast implementation of a proper architecture where the developer can iterate quickly and | ||
and with the ability to use good practices as SOLID and TDD from day 0. | ||
|
||
Kor is not meant to be a canonical implementation of such architecture, but a personal interpretation of how it should be. | ||
|
||
How is Kor structured? | ||
====================== | ||
|
||
Basic concepts | ||
-------------- | ||
|
||
Kor relies on the classic concept of "Use Cases". Each "Use Case" comprises a full request to the model. This request | ||
can be to Cache, Network or other types of aynchronous running operations. By this, the UI layer is completely decoupled | ||
of the rest of the Model logic. | ||
|
||
A key feature of Kor is that any element of the model is fetched from a `Request` and by using the `Repositories`, the | ||
Model elements are single-instanced (the same object is returned always for the same unique ID) and updating the model | ||
is done automatically thanks to Java pointer to object features. | ||
|
||
### Providers | ||
|
||
The communication is done via executing those Requests which are Command patterns into a Provider element. This is an | ||
execution queue abstracted, whose responsibility is to take the business decision on when to actually execute the command. | ||
|
||
The `Provider` interface provides ways to execute single or multiple requests. Again it is up to the concrete implementation | ||
of the `Provider` to encapsulate such decisions. | ||
|
||
### Requests | ||
|
||
A `Request` is the equivalent of an `Interactor` in terms of functionality. The `Request` is characterized for definining | ||
two types of responses in the default implementation, a `Response` and an `Error`. Those are nothing more than messages | ||
that define the success or a failure of such `Request`. | ||
|
||
A failure can happen by many reasons, being unexpectedly from an Exception of the program or by a controlled situation | ||
during the flow of the `Request`itself. However, a response gives the chance to provide a successful, but not satisfiable | ||
termination by means of `isSucess()` method. | ||
|
||
An external element of the architecture will be listening somehow for those responses, it is part of the contract. However | ||
it is not strongly bound, and depending on the requirements a Request sent by a component can be listened by a completely | ||
unrelated one. | ||
|
||
A typical Request should (but it is not enforced to) implement all these stages. In the case of a `NetworkRequest`: | ||
|
||
* **retrieveNetworkResponse:** In this stage the code belonging to fetch the information from the server, API or webservice. | ||
A successful completion of this stage should already generate a Response-type result with the results converted into POJO | ||
via JSon deserialization or any other means necessary. | ||
* **postProcess:** This stage is meant to analyze the data and give the chance to set the success flag to false or other | ||
crossing from the data. | ||
* **saveToCache:** This stage is provided to encapsulate all the necessary logic to persist the information to memory or disk. | ||
* **composeErrorResponse:** This stage will only happen in exceptional situations which will lead to a failed execution. The | ||
idea is to analyze the exception and output an ErrorResponse which the UI layer can use to give the user detailed information. | ||
|
||
For heavy-duty operations, `FastSaving` interface is available to provide a chance to make a preliminary persistence to | ||
memory and notifiy the user to give out the response while offloading to a slower cache in the background of the `Request`. | ||
|
||
The case of the `CacheRequest` the behavior is slightly different. A `CacheRequest`is supposed to fetch the information | ||
from the persistence system. As such, it lacks `postProcess` and `saveToCache` stages. In the earlier step, because the | ||
information is supposed to have been saved on the system via an early `NetworkRequest`, and as such, having passed through the | ||
necessary steps. In the latter, for obvious reasons. | ||
|
||
* **isCacheValid:** This stage is meant to be executed _before_ the actual retrieveFromCache, and it is meant to peek at | ||
the cache data to know in advance if the information is valid (e.g. has not expired or it is present) before trying to | ||
retrieve the data | ||
* **retrieveFromCache:** As stated this is the stage for actually getting the information from the cache. | ||
|
||
### Strategies | ||
|
||
A `Strategy` backbone responsibility is to notify when the `Request` has completed sucessfully or not. An implementation | ||
of a Strategy will delegate on a `Request` to do all the work, and will simply execute the request steps in a certain order. | ||
|
||
The module kor-retrofit implements several default strategies for most general cases: | ||
|
||
* **CacheExecutionStrategy:** Retrieves the information form the persistence system. If the content is no longer valid it | ||
does not notify of anything to reduce the noise during the execution. | ||
* **StandardNetworkStrategy:** Retrieves the information from network, postprocesses it, saves it into the persistence and | ||
notifies it. | ||
* **FastSaveNetworkStrategy:** Retrieves the information from the network, postprocesses it, saves into a fast cache (typically | ||
memory), notifies to the result and then resumes the saving to the persistence. | ||
|
||
### Notification System | ||
|
||
Kor does not enforce any particular system of notification. It can be used via callbacks that implement the `Postable` | ||
interface. However, **we encourage to use an Event Bus system** (Otto or Greenrobot's) or **RxJava** in order to provide a real | ||
decoupled architecture. | ||
|
||
### Repositories | ||
|
||
Kor provides a persistence abstraction via a _Repository_ pattern. This is nothing more than a CRUD interface. | ||
|
||
The basic implementations available are nothing more than a Key, Value implementation on a memory Map for both normal and `FastSaving` | ||
requests. | ||
|
||
The Repositories are also built on a _Chain of Responsibility_ command. Can work standalone or chained together to provide | ||
multitier persistence systems on both memory and disk implementations. | ||
|
||
Kor-Android module also provides a LRUMemory implementation. | ||
|
||
For a model element to be usable with repositories, it requires to implement both `RepoElement` interface to provide | ||
an unique ID to save and get from the Repository and a Updateable implementation to update information inside the Model | ||
element itself. | ||
|
||
Kor Flavors | ||
=========== | ||
|
||
* **Kor-Common:** The backbone of the Kor Architecture. It is useable, as long as you are willing to implement most of | ||
the things. It is also a good point to extend the architecture to suit your needs. | ||
* **Kor-Android:** Adds Android extensions for the Kor architecture, providing a LRUCache-based Repository in addition of | ||
the basic Map one. | ||
* **Kor-Retrofit:** Adds extensions for using the [Retrofit Library](https://github.com/square/retrofit). Includes the | ||
default Strategies oriented to Retrofit, although they can be backported 99% to basic Kor. | ||
|
||
Interesting literature | ||
====================== | ||
|
||
Martin, Robert Cecil: [The Clean Architecture](http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html) | ||
Cockburn, Alistair: [Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) | ||
[VIPER Architecture](http://mutualmobile.github.io/blog/2013/12/04/viper-introduction/) | ||
|
||
License | ||
------- | ||
Copyright 2014 Sefford. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
|
||
|