Lesan solution
As mentioned in the above section, Lesan
is a collection of models
and acts
created for those models
. But in fact, these models
are placed inside another object called main
or any other service
that is active in this process. All the information available in the main
key can be obtained with the getMainActs
function. All functions created with the setAct
function are stored by default inside the main
object. But there is another function called setService
. With this function, we can add another service
to our project. After adding a new service, we can access it by sending an object
from the client side that has a service key
. The setService
function has two inputs: the first input is the name
of the service and the second input can be obtained in two ways:
-
As a
string
, which is actually the address of access to anotherservice
. -
As another
object
, which is actually the output of thegetMainActs
function in anotherservice
.
If the second input is a string
, http
or rpc
methods are used to communicate with it, and if it is an object
, that other service comes up as a plugin
on this current service. As a result, we can manage a project both as a monolith
and as a microservice
at the same time.
A suggestion for microservices (an architecture between microservices and monolith)
To create a model
in Lesan
, it can be expanded based on another extensive model
so that it has nothing more than that and can only have some of its keys
. Therefore, we can create a database
for all services
along with all models
that have all the necessary keys
in all services
. Then each service
defines its own model
separately based on the integrated database
model and takes some of its required keys
from that main
model. Given the way models
are written in Lesan
(model implementation based on a schema validator
), we can have a common database
and at the same time each service
can validate
its own data
based on the expanded model
of the comprehensive and original model
. Also, it is possible to move models
and acts
written in Lesan
, and we can easily have each service’s database
separately or simultaneously with other services
. On the other hand, NoSQL
databases are usually schemaless
and any shape or form can be given to data
in the database. Lesan
is currently developed based on MongoDB
. If we can put all service
models in a comprehensive database
, we can prevent data duplication
and we no longer need to write parallel logic
to manage this duplication
. In addition, we do not need any tools for synchronization or writing separate logic
to standardize data
. Finally, we can also take advantage of the ease of horizontal-distribution
of NoSQL
databases without worrying about data
consistency. Consider the following example:
Suppose we have several services named core
- ecommerce
- blog
and so on, all of which have a model for users named user
. We can create a model of the user
that has all the fields of all these services
and share it among all services, like this:
import {
any,
array,
boolean,
date,
enums,
InRelation,
number,
object,
optional,
OutRelation,
string,
} from "../../../deps.ts";
export const level = enums(["Admin", "Editor", "Author", "Ghost", "Normal"]);
export const gender = enums(["Male", "Female"]);
export const addressObj = object({
addressId: string(),
countryId: string(),
stateId: string(),
cityId: string(),
addressText: string(),
location: optional(
object({
type: string(),
coordinates: array(array(number())),
})
),
});
export const pureUserObj = {
_id: optional(any()),
name: string(),
age: number(),
lastName: string(),
phone: number(),
gender: gender,
birthDate: optional(date()),
postalCode: string(),
level: array(level),
email: optional(string()),
isActive: optional(boolean()),
creditCardNumber: optional(number()),
address: optional(array(addressObj)),
};
const userRelations = {
country: {
optional: false,
schemaName: "country",
type: "single" as RelationDataType,
relatedRelations: {
users: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "_id",
order: "desc" as RelationSortOrderType,
},
},
}
},
province: {
optional: false,
schemaName: "province",
type: "single" as RelationDataType,
relatedRelations: {
users: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "_id",
order: "desc" as RelationSortOrderType,
},
},
}
},
city: {
optional: false,
schemaName: "province",
type: "single" as RelationDataType,
relatedRelations: {
users: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "_id",
order: "desc" as RelationSortOrderType,
},
},
}
},
};
Now, for example, we create a model of the user for ecommerce as well and write its fields in such a way that it does not have anything more than the shared user model, like this:
import { ecommerceApp } from "../../../../../apps/ecommerce/mod.ts";
import {
any,
array,
boolean,
date,
InRelation,
number,
optional,
OutRelation,
string,
} from "../../../deps.ts";
import {
addressObj,
gender,
level,
pureUserObj as sharedPureUserObj,
userRelations as sharedUserRelations,
} from "../../shared/mod.ts";
const userPureObj: Partial<typeof sharedPureUserObj> = {
_id: optional(any()),
name: string(),
age: number(),
lastName: string(),
phone: number(),
gender: gender,
birthDate: optional(date()),
postalCode: string(),
level: array(level),
email: optional(string()),
isActive: optional(boolean()),
creditCardNumber: optional(number()),
address: optional(array(addressObj)),
};
const userRelations: Partial<typeof sharedUserRelations> = {
// country: {
// optional: false,
// schemaName: "country",
// type: "single" as RelationDataType,
// relatedRelations: {
// users: {
// type: "multiple" as RelationDataType,
// limit: 50,
// sort: {
// field: "_id",
// order: "desc" as RelationSortOrderType,
// },
// },
// }
// },
// province: {
// optional: false,
// schemaName: "province",
// type: "single" as RelationDataType,
// relatedRelations: {
// users: {
// type: "multiple" as RelationDataType,
// limit: 50,
// sort: {
// field: "_id",
// order: "desc" as RelationSortOrderType,
// },
// },
// }
// },
city: {
optional: false,
schemaName: "province",
type: "single" as RelationDataType,
relatedRelations: {
users: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "_id",
order: "desc" as RelationSortOrderType,
},
},
}
},
};
Now we can connect them to a common database
while several services
continue their work independently, provided that the data validation
of the schemas works independently for each service and only understands its own data
. You can see a complete example of this type of microservice implementation here.