• This repository has been archived on 21/Nov/2018
  • Stars
    star
    666
  • Rank 67,706 (Top 2 %)
  • Language
    Java
  • License
    MIT License
  • Created over 8 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Chateau is a framework for adding (or improving) chat functionality in any Android app

Update: Preview3 is out now, containing significant (breaking) changes to almost all presenters and data source definitions. More detailed information about the change will be posted soon.

Chateau

Chateau is a framework for adding (or improving) chat functionality in any Android app. Built in a modular way using MVP and Clean Architecture, it can easily be integrated with your chat backend with only minor changes to the included UI.

Background

Chat has always been a major component of the apps we develop at Badoo. Over the years we have gone through multiple rewrites and refactorings of our core chat code but up until now we have always kept it under wraps. With project Chateau we aimed to create a great chat experience, and not just for us but for everyone.

Our goals when we set out were as follows:

  • Easy to understand code, by consistently applying design patterns accross the framework and example app
  • Easy to integrate with any chat backend
  • Well documented with good test coverage
  • As few as possible external dependencies, because no one likes a bloated library
  • Open source, because that's how we roll at Badoo

If you're reading this as an iOS developer, some of our engineers already implemented a similar framework for iOS. The result of their efforts can be found here.

Architectural overview

Clean Architecture

The architecture of the app is based upon the concept of Clean Architecture put forward by Robert Martin, which we adapted to fit our needs. In a Clean Architecture, the code is divided into layers where each layer must only have dependencies to lower layers (or as shown in the diagram below, dependencies going towards the right). This in combination with the Model-View-Presenter (MVP) pattern has allowed us to divide the code into components which can be individually tested by mocking dependencies to lower layers.

Chateau is divided into 3 layers:

Presentation Layer

This layer contains all UI related logic (Views and Presenters), it knows nothing of the data layer and is only able to perform actions by invoking usecases that resides in the Domain layer. As with the other layers, the presentation layer is interchangable without affecting the other two layers. All results from use cases are observed (using RxJava) on the main thread to ensure threading consistency and make it easy to update the UI.

Domain Layer

Contains the application's use cases which encapsulate application specific business rules, for example SignIn or SendChatMessage would be an example of use case. Use cases are able to query the data layer (Repositories), but should know nothing of the implementation of that layer. All subscriptions to the data layer are made on the computation thread.

Data Layer

Finally the data layer contains interaction with the network, databases or other locations where data can be retrieved or stored. Changing the underlying implementation should not affect the other two layers. The data layer can also consist of several different sources chained together (i.e memory cache, disk cache and finally network request for loading conversations).

Further reading

As the above only gives a rough overview of clean architecture I'd strongly recommend checking out the following:

Using Repositories and Datasources

The data layers consists of repositories and data sources. Generally repositories are used to map queries to data sources. The aid with this, the DelegatingRepository.java has been created, which allows query handlers to be registered for each query which in turn map them to a data source. It is also possible to annotate a data source using the Handles.java annoatation. Currently this is progressed using reflection, but there is a future task to before this via an annoation processor.

It is important to note, that the Query has a generic type. This at first may seem redundent, but it used to type the result type when used against a repository. Initially repositories could only return a single type, which didn't really make sense in most situations, and also let to the point where most repositoties returned a list of a type, when most of the time only the single of that type was needed.

Generally we model our datasource in the following way:

  • A subscription method, which returns the current data that data source contains. (Note in some rare cases multiple subscription methods will be needed, however this generally indicates that the data source should be split)
  • Command methods which cause modification to the data in the data source. Once the modification is complete, the current state of the data should be published to any subscribers.
  • Query methods which query data contains in the data source. These may or may not return some data, but will not cause data to be published to subscribers.

Examples of the configuration of Repositories and Datasources can be found in ChatExampleApp.java.

How we MVP

There are many different approaches that can be taken to implement MVP (Model-View-Presenter) on Android. The one we selected is something that we have been iterating on internally at Badoo over the last 6 months.

interface Presenter {
	
	void onUserDidSomething();
	
	void onUserDidSomethingElse(int what);
	
	interface View {
		void showA(A a);
		void showB(B b);
	}
}

Presenters and Views are defined together as interfaces, giving us a clear overview of all the interactions that can occur between the two. By always using interfaces it is also very easy to replace the actual (Presenter or View) implementation with a mock when testing.

Another decision we made was to always keep the View implementation separate from the Activity or Fragment which is using it. This allows the view to be easily used in either one.

View and Presenter implementations

When creating an implementions of views and presenters we follow a certain set of practices.

  • The following is passed to the presenter constructor:
    • The View implementation (by its interface)
    • The use cases the presenter requires to operate
  • The view implementation is given via it's constuctor
    • A PresenterFactor which is used to instantiate the presenter. This allows both the view and presenter to have access to each other in their constuctors. Because of this pattern, calls to the view's methods during the constuction of the presenter should be avoided.
    • A ViewFinder is generally used to find the views as it allways easy access to finding views by id whether the view owner is a Fragment or an Activity.

Note: When adding functionality to a screen, it is prefered to additional presenters (composition) rather than extending the current presenter, however there is times where the later is preferable.

Wiring it all together

We decided to keep it simple and simply wire the app together by hand, you can see how in the ChatExampleApp.java. However of course a DI framework such as Dagger2 could be used.

Project overview

Chateau is split into several modules, each one (except the example app) published separately to jCenter. For instructions on how to include them in your build see the Integrating Chateau in your app section.

BARF

The Badoo ARchitectural Framework provides the foundation of Chateau. It contains definitions of Model, View and Presenters as well as Use cases and Repositories.

ChateauCore

Contains interface definitions for the chat related model classes (such as Message, Conversation, etc), use cases dealing with these models and the Repository and DataSource definitions needed to provide basic chat functionality. This module only contains core functionality which can be expected to be useful in any chat implementation.

If you are looking to build a more customized chat experience (without the extra functionality provided by the main Chateau library) then consider building it on top of ChateauCore instead.

Chateau

The main library containing implementations for the different Presenters which together provides the chat experience (Listing, creating and deleting conversations, listing and sending messages, retrieving information about users). In addition to this it contains implementations for the models defined in ChateauCore.

ChateauExtras

Contains any useful utility classes that are not part of the core framework, at some point this might be moved to a seperate library outside of the Chateau project.

ExampleApp

A simple example app which shows how to to integrate Chateau with your UI and backend. Contains the concrete View and DataSource implementations as well as the UI (Activities and layouts).

Third party dependencies

Our initial goal was to have no external dependencies except for the Android SDK. The reason being that we did not want to force any additional dependencies on someone looking at integrating the chat framework into their application. However, after weighing the alternatives of using RxJava compared to rolling our own framework we eventually decided upon using RxJava.

The main reasons for this descision were:

  • Communication between the architectural layers (Data source → Repository → Use case → Presenter)
  • Provide thread boundraries to ensure that the correct thread is always used (for heavy operations, IO and UI updates)

Example Application

The Chateau example app provides a showcase for how to integrate the Chateau framework. It contains a reference chat UI implementation (supporting text and image messages, group chats and more) coupled with an back-end integration layer that communicates with a very simple chat server using HTTPS and sockets (server source also comes bundled with the app).

In order to run the example app you will need to have a backend to connect to. For this there are two options: either connect to our testing backend or set up your own.

Connecting to our testing server

There is no special configuration needed to do this (the example app is already configured for this endpoint) but please be aware that we do not make any guarantees regarding the data on the testing server. Any user created there as well as any conversation or message may be deleted at any point, at our discretion.

Setting up the example app chat server

Please note: The chat example backend currently runs on Parse. Unfortunately, just after we got the backend up and running it was announced that Parse is closing down in the beginning of 2017. We are currently evaluating our options for either migrating to a different service or creating a stand alone example server.

The quickest way to get up and running with the example chat backend is to import it into your Parse account (only possible if you have an existing account as Parse is no longer accepting new registrations). If you don't already have a Parse account the other option is to set up a stand alone instance of parse-server.

A zip archive containing the json definition of the required Parse classes can be found in the ExampleApp/files directory. There you can also find the CloudeCode source code.

The last step you need to take it to set up the socket notification proxy. This lightweight server acts as a bridge between HTTP (request coming from the backend) and socket notifications (sent out to connected clients). This open source project can be found here. Please note that you need to run it on a computer that is accessible (using port 9000) to the devices running the chat example app.

Now that you got the server up and running it's time to point the app towards the two endpoints. In ChatExampleApp/build.gradle you will find the following three build configuration keys (found in ExampleApp/build.gradle):

  • PARSE_APP_ID
  • PARSE_CLIENT_KEY
  • SOCKET_NOTIFICATION_ENDPOINT

Integrating Chateau in your app

In order to get your basic chat functionality up and running there are two main components that needs to be added.

  • Backend integration (in the form of data sources)
  • UI (for displaying different types of messages as well as for handling composing the messages)

Backend integration

To integrate Chateau with your backend you will need to create implementations of the following DataSources:

  • com.badoo.chateau.core.repos.messages.MessageDataSource
  • com.badoo.chateau.core.repos.conversations.ConversationDataSource
  • com.badoo.chateau.core.repos.istyping.IsTypingDataSource
  • com.badoo.chateau.core.repos.users.UserDataSource

The next step is to make sure that instances of the repositories for these data sources are available to the Use Cases that needs them. Exactly how you want to do this is up to you but here are some suggestions:

  • Singletons (easy but could make testing a bit messy)
  • Use a dependency injection framework (e.g. Dagger 2)
  • Instantiate when needed (could work if they have no state)

In the example app we are using a simple configuration class that contains references to all the needed repositories. Please check com.badoo.chateau.example.ui.ExampleConfiguration and com.badoo.chateau.example.ChatExampleApp for an example of this.

TIP: If you want to add functionality to the built-in presenters we would in most cases recommend that you add a separate presenter or a composite presenter. However, the base class can also be extended using inheritance as long as you take care when dealing with the generic parmeters for the View type.

UI Integration

The main part of the UI integration work is to implement the Views (MVP Views that is) that are matched up with the Presenters defined in the Chateau library.

For basic functionality (to be able to list the current conversations and read and send messages in a conversation) you will need to implement the following views.

  • com.badoo.chateau.ui.chat.input.ChatInputPresenter.ChatInputView
  • com.badoo.chateau.ui.chat.messages.MessageListPresenter.MessageListView
  • com.badoo.chateau.ui.conversations.list.ConversationListPresenter.ConversationListView

Optionally you can also implement the following views if you want to be able to pick an user to start a conversation with.

  • com.badoo.chateau.ui.conversations.create.selectusers.UserListPresenter.UserListView
  • com.badoo.chateau.ui.conversations.create.namegroup.NameGroupPresenter.NameGroupView

After doing this you will need to make sure that both the presenters and views are created in your Activity or Fragment. The following simple example shows how this could be done in the activity's onCreate() method.

final PresenterFactory<RegistrationView, RegistrationPresenter> presenterFactory = new PresenterFactory<>(v -> createRegistrationPresenter(v, target));
new RegistrationViewImpl(..., presenterFactory);
presenter = presenterFactory.get();

In the example above the createRegistrationPresenter(...) method creates the presenter and passes in the use cases that are needed. These use cases in turn needs access to the repositories that you previously configured (see Backend Integration for more details).

Who are we?

Many people at Badoo have contributed greatly to the Chateau project in one form or other (Thanks go out to everyone contributing to the architecture as well as our awesome QA team!).

Erik

I'm a senior Android developer at Badoo with 7 years of experience creating awesome apps for mobile devices. Check out my Github profile for other projects that I'm involved in.

Rich

I'm also a senior Android developer at Badoo where I've been since moving to London. I have been passionate about Android since its release and my first Android phone (HTC Hero!). Outside of programming I enjoy music (post-rock, metal) especially gigs and festivals, I'm an avid cycler and love to travel. You can find my Github profile here.

Roadmap

  • Migrate example app backend from Parse
  • Use annotation processing instead of reflection when creating delegating respositories

License

Source code is distributed under MIT license.

Blog

Read more on our tech blog or explore our other open source projects

More Repositories

1

Chatto

A lightweight framework to build chat applications, made in Swift
Swift
4,476
star
2

android-weak-handler

Memory safer implementation of android.os.Handler
Java
1,544
star
3

MVICore

MVI framework with events, time-travel, and more
Kotlin
1,253
star
4

Reaktive

Kotlin multi-platform implementation of Reactive Extensions
Kotlin
1,172
star
5

MVIKotlin

Extendable MVI framework for Kotlin Multiplatform with powerful debugging tools (logging and time travel), inspired by Badoo MVICore library
Kotlin
824
star
6

Decompose

Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI (Jetpack Compose, SwiftUI, JS React, etc.), inspired by Badoos RIBs fork of the Uber RIBs framework
Kotlin
817
star
7

soft-mocks

PHP mocks engine that allows to redefine functions and user methods on-the-fly (provides similar functionality to runkit and uopz extensions)
PHP
310
star
8

liveprof

A performance monitoring system for running on live sites
PHP
232
star
9

phpcf

PHP Code Formatter
PHP
183
star
10

lsd

Live Streaming Daemon
Go
169
star
11

RIBs

Badoo's take on RIBs
Kotlin
162
star
12

BMASpinningLabel

BMASpinningLabel is an UI component which provides easy way for displaying and animating text inside it
Objective-C
151
star
13

pinba2

Pinba2: new implementation of https://github.com/tony2001/pinba_engine
C++
131
star
14

hprof-tools

Tool for deobfuscating memory dump files
Java
127
star
15

liveprof-ui

An aggregator and web interface for Live Profiler
PHP
126
star
16

ios-collection-batch-updates

Safely perform batch updates in UITableView and UICollectionView
Objective-C
123
star
17

BMAGridPageControl

Objective-C
100
star
18

BMASliders

Configurable simple and range sliders
Objective-C
93
star
19

codeisok

Git code browsing and code review tool
PHP
85
star
20

KmpMvi

Sample of MVI in Kotlin Multiplatform
Kotlin
70
star
21

MockJS

JavaScript
67
star
22

jira-client

Badoo JIRA API Client with code generator
PHP
66
star
23

balancer

Load balancer that was presented at HighLoad++ 2015 Conference in Moscow
PHP
44
star
24

ios-device-server

A server to manage remote iOS simulators and devices for parallel testing
Kotlin
42
star
25

thunder

Our cloud system
Go
42
star
26

funcmap

PHP extension that logs all called userspace functions/methods
C
39
star
27

FreehandDrawing-iOS

A tutorial to build a freehand drawing feature on iOS.
Swift
38
star
28

StarBar

Java
35
star
29

libpssh

library implementing asynchronous SSH connections
C
28
star
30

THEPageControl

Swift
21
star
31

parallel_cucumber

Ruby
21
star
32

BMACollectionViewLayouts

A set of UICollectionView layouts
Objective-C
20
star
33

HyperLabel

Swift
16
star
34

uiautomatorviewer

Rebuild of UiAutomatorViewer app from the sources to make it compatible with modern JDK versions
Java
15
star
35

Gallery

Swift
15
star
36

techblog

HTML
15
star
37

ReceptionApp

Application for guests to sign in and sign off while in the office
Objective-C
15
star
38

ssmtp

extremely simple MTA to get mail off the system to a mail hub
C
13
star
39

pssh_extension

PHP extension-wrapper for libpssh
C
13
star
40

rtl-css

JavaScript
12
star
41

habr

Materials for habrahabr articles
PHP
11
star
42

xhprof_console

A console tool for grabbing profiles from XHProf database and collecting aggregates from them
PHP
11
star
43

libssh2

libssh2 clone with additional patches applied
11
star
44

styleguide

Badoo styleguide used to develop UI components for the Web and React Native
JavaScript
10
star
45

kexasol

Exasol database driver implemented in Kotlin (JVM). It is based on native WebSocket API, supports parallel CSV streaming and compression.
Kotlin
10
star
46

MobileAutomationSampleProject

A sample project to demonstrate best practices for a mobile automation using Cucumber framework
Ruby
8
star
47

intellij-idea-live-profiler

A PhpStorm plugin for Live Profiler.
Kotlin
7
star
48

exasol-data-lineage

Exasol data lineage scripts
Python
6
star
49

TooltipsQueue

Kotlin
6
star
50

phpunit-testlistener-teamcity

Reporting Test in TeamCity using Service Messages
PHP
5
star
51

tarantool-dissector

Wireshark's dissector for the Tarantool's protocol
Lua
5
star
52

dust2jsx

Convert Dust.js templates to JSX
JavaScript
3
star
53

coverage-service

Create code coverage report from window.__coverage__ object
JavaScript
3
star
54

uap-php-lite

PHP implementation of ua-parser without runtime dependencies
PHP
2
star
55

idea-printf-checker-plugin-example

Kotlin
2
star
56

file-streamer

Streams given file data into any buffered writer. Uses fsnotify system for new data detection in files.
Go
2
star
57

hadoop-xargs

Util to run heterogenous applications on Hadoop synchronously
Java
2
star
58

DeviceAgent.iOS.Inspector

Web inspector of UI elements for iOS DeviceAgent
JavaScript
1
star
59

badoo.github.com

PEAR Channel
1
star
60

centrifugo-bench

Benchmark tools for centrifugo
Go
1
star
61

app-tree-utils

Kotlin
1
star
62

meow

C++
1
star