WORK IN PROGRESS
Phenyl is a series of JavaScript libraries for both client-side and server-side, which focuses on State Synchronization over Environments(SSoE).
ServerSide Phenyl provides an API server like MBaaS(Mobile Backend as a server). ClientSide Phenyl provides Redux module system. Within these two environments, States are Synchronized in various ways.
State Synchronization over Environments is the main concept of modern applications.
For example, if you use RESTful API, you can synchronize server and client state through following HTTP methods:
- Acquire server state of user by GET
/user
and provide user profile view by acquired user entity. - User updates user name at profile view, app update user entity in the client side and app POST or PATCH
/user
. - User delete account, app clear the client state and app execute DELETE
/user
It's popular method of SSoE. So, many people create RDB, write API server to transform Data to Entity and communicate with client, write fetch code in client, client transform Entity to data for view, client update entity and notify to server, server save updated entity to DB and so on. Additionally error handlings are neccessary in practice.
Phenyl provides simpler solution to handle SSoE based on the following 4 concepts:
- State as one big JSON
- OAD: Operations as Data
- MongoDB-like operations
- Git-like synchronization
JSON is very useful format for JavaScript. It has some basic types like string or boolean and it's easy to serialize and deserialize. We represent the entire app state as one big JSON.
Phenyl handles all CRUD operations on data, such as creating an entity, reading the data in an entity, updating a property in an entity, and deleting an entity as data. The history of all operations is also saved as in the state. This concept allows us to easily reproduce and debug any situations we want.
Phenyl provides MongoDB-like operations.
In a mongoDB shell, CRUD operations are performed as following:
> db.testUser.insertOne({_id: "test1", name: "Test1"})
{ "acknowledged" : true, "insertedId" : "test1" }
> db.testUser.findOne({_id: "test1"})
{ "_id" : "test1", "name" : "Test1" }
> db.testUser.updateOne({name: "Test1"}, {$set: {favoriteFood: "banana"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.testUser.findOne({_id: "test1"})
{ "_id" : "test1", "name" : "Test1" , "favoriteFood": "banana" }
> db.testUser.deleteOne({"_id": "test1"})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.testUser.findOne({_id: "test1"})
null
In Phenyl client, the operations are performed as following. (This code is simplified.)
// omission prepare httpClient and preSession
const inserted = await phenylClient.insertAndGet(
{
entityName: "testUser",
value: {
id: "test1",
name: "Test1",
}
},
preSession.id
);
console.log(inserted.entity)
// -> { id: "test1", name: "Test1" }
const updated = await phenylClient.updateAndGet(
{
entityName: "testUser",
id: "test1",
operation: {
$set: { favoriteFood: "banana" }
}
},
preSession.id
);
console.log(updated.entity)
// -> { id: "test1", name: "Test1" , faboriteFood: "banana" }
await phenylClient.delete({ entityName: "testUser", id: "test1" })
If you have used mongodb, you will soon get to be friendly with the API of Phenyl😎.
Phenyl synchronizes between server and client by using git-like command.
You can acquire server side entity by pull
and update by push
.
This allows us to handle offline oepration easily.
- @phenyl/rest-api: REST API logic. Core part of Phenyl.
- @phenyl/http-server: Simple HTTP Server to host PhenylRestApi.
- @phenyl/express: Express middleware to run PhenylRestApi on it.
- @phenyl/websocket-server: WebSocket server to emit update operations of entities.
- @phenyl/mongodb: MongoDB client.
- @phenyl/lambda-adapter: Helper to run PhenylRestApi on AWS Lambda.
- @phenyl/memory-db: volatile memory DB for local mocking.
- @phenyl/http-client: Client to PhenylRestApi.
- @phenyl/redux: State synchronization among Phenyl CentralState(server) and LocalState(client) using Redux.
- @phenyl/websocket-client: WebSocket client to listen to entities's update.
- @phenyl/interfaces: All types of Phenyl family are defined here.
- @phenyl/standards: Provides advanced features.
- @phenyl/utils: (Almost internal) Utility functions in Phenyl family.
- @phenyl/state: (Almost internal) Entity state Reader/Writer.
- @phenyl/http-rules: (Almost internal) Set of rules that translates HTTP Request/Response into Phenyl RequestData/ResponseData.
Phenyl is powered by sp2, a set of JavaScript modules used for state operations.
Phenyl needs you to implement 2 features, one is GeneralTypeMap and the other is functionalGroup. GeneralTypeMap is type definition that describes shape of request and response of each entity and auth information. functionalGroup is implementation to notify Phenyl about the domain that we want to use. If you want to know more details, see example.
Apache License 2.0