Generic feed adding social features to current applications.
RabbitMQ as event producer. This app consumes, stores, and serves. The goal is to create a plug-n-play social feed for existing applications. May be easy to consume events from other sources.
This is an OTP umbrella application, containing other 3 OTP applications within the apps folder.
/apps/bus
/apps/store
/apps/web
Buscontains the RabbitMQConsumergeneric server, which responsibility is to store events into the feed.
FeedRepois the main data store for our feed. Can be backed by any ecto's supported databases.SourceRepocontains a connection to other app database, in which user's are stored. We should be able to get user name, id, and profile pic from one table. The store app implements a simple caching using con_cache, so that we don't hit theSourcedatabase too much. The caching mecanisms and access to the database can be configured, if we don't setup a TTL, each user will only be looked up once per app restart. (We can avoid apps restart using erlang's code hot swap 🙂)Feedgen_server is the main API for consuming the feed.FeedBuildergen_server is an API for building up the feed.Usersgen_server retrieves and caches users given an id. Mostly accessed by the feed server.Commentsgen_server is an API for showing, creating, updating, and deleting comments.Likesgen_server is an API for liking and unliking feed events.
- The web project contains a json API and websockets for consuming and updating the feed. Uses phoenix framework
An app should post events to RabbitMQ with a given format, in order for this app to consume and process events to build the feed. We can always shutdown AMQP, and feed the Store.FeedBuilder using distributed erlang, the API, or building some other mecanism.
We support 3 kind of events. create, update, delete. In order for us to handle an update or delete correctly, we should have already processed the create. udpate wont work once the event is deleted.
{
"event": {
"type": "create",
"tenant": 0,
"user_id": 0,
"content": "Main data content",
"extra": {},
"date": "ISOz date"
}
}
GET @ /api/v1/feed
{
data: [{
"id": 1,
"user": {
"id": 1,
"full_name": "Nombre",
"profile_pic": "profile"
},
"type": "post",
"content": "some content",
"comments": {
"total": 10,
"preview": [{
"user":...,
"comment": "hello world!"
}]
}
}]
}
GET @ /api/v1/feed/:tenant_id
{
data: [{
"id": 1,
"user": {
"id": 1,
"full_name": "Nombre",
"profile_pic": "profile"
},
"type": "post",
"content": "some content",
"comments": {
"total": 10,
"preview": [{
"user":...,
"comment": "hello world!",
"id": 1
}]
}
}]
}
GET @ /api/v1/feed/:tenant_id/event/:event_id/comments
{
data: [{
"user": ...,
"comment": "Content",
"id": 1
}]
}
POST @ /api/v1/feed/:tenant_id/event/:event_id/comments
{
content: "Hello world!"
}
DELETE @ /api/v1/feed/:tenant_id/event/:event_id/comments/:comment_id
GET @ /api/v1/feed/:tenant_id/event/:event_id/like
GET @ /api/v1/feed/:tenant_id/event/:event_id/unlike
We can also subscibe for updates using phoenix channels.
Websocket @ /ws/v1/feed
Subscribe for company events @ company:<id>
// new event
{
"type": "create",
"data": {},
"id": 0
}
// update event
{
"type": "create",
"data": {},
"id": 0
}
// delete event
{
"id": 0
}
Subscribe for event changes @ event:<id>
// new comment
{
"type": "create_comment",
"data": {},
"id": 0
}
// update comment
{
"type": "update_comment",
"data": {},
"id": 0
}
// delete comment
{
"type": "delete_comment",
"id": 0
}
// new like state
{
"type": "update_likes",
"data": {}
}
// someone is typing
{
"type": "typing",
"data": {}
}
Change RabbitMQ config on apps/bus/config/{dev, test, prod}.exs.
config :bus, :rabbitmq, "amqp://guest:[email protected]"
Change feed data store settings on apps/store/config/{dev, test, prod}.exs
config :store, Store.FeedRepo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "store_db_dev",
hostname: "localhost",
pool_size: 10
Setup your app's database connection, and the name of the table and columns to pull the users parameters.
config :store, Store.SourceRepo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "store_db_dev",
hostname: "localhost",
pool_size: 10
config :store, :external_db_table_name, "tabla"
config :store, :external_db_full_name, :full_name
config :store, :external_db_user_id, :id
config :store, :external_db_profile_pic, :image_url
Cache mecanism for users and other feed interactions can be customized. TTL is not active by default.
config :store, :user_cache,
ttl: true,
touch_on_read: true,
global_ttl: 20,
check_interval: 2
config :store, :interactions_cache,
ttl: false
- Better docs
- More tests
- Generic authentication and authorization mecanism
- Explore other ways of retrieving user's data.
- Explore other ways of storing data. Cassandra, Riak, Mnesia 🤔