Skip to content

Commit 8554347

Browse files
authored
Docs/usage documentation (#11)
* docs: initial usage documentation * docs: add main framework documentation * docs: add documentation for each service kind
1 parent 5556f46 commit 8554347

File tree

8 files changed

+356
-4
lines changed

8 files changed

+356
-4
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ standalone applications that execute its task and finishes.
1414

1515
Currently, it has support for the following kind of applications:
1616

17-
* gRPC: an application with an API defined from a [protobuf](https://protobuf.dev) file.
18-
* HTTP: an HTTP server-type application.
19-
* native: a general-purpose application, without a defined API, with the ability to execute any code for long periods
20-
* script: also a general-purpose application, without a defined API, but that only needs to execute a single function and stop.
17+
* [gRPC](docs/service_grpc.md): an application with an API defined from a [protobuf](https://protobuf.dev) file.
18+
* [HTTP](docs/service_http.md): an HTTP server-type application.
19+
* [native](docs/service_native.md): a general-purpose application, without a defined API, with the ability to execute any code for long periods
20+
* [script](docs/service_script.md): also a general-purpose application, without a defined API, but that only needs to execute a single function and stop.
21+
22+
### Documentation
23+
24+
A more detailed documentation about the framework API and its features can be
25+
accessed at the [docs](docs/mikros.md) directory.
2126

2227
### Service
2328

@@ -125,6 +130,7 @@ using service definitions.
125130
* Enable features and services to use the same env system that the core uses.
126131
* Improve unit tests.
127132
* Full compatibility between go and rust gRPC services.
133+
* Allow using the service definitions file for custom service definitions.
128134

129135
## License
130136

docs/mikros.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# mikros
2+
3+
## Introduction
4+
5+
Mikros is a rust framework to help writing services or applications following the
6+
same code structure and layout. It makes these applications demonstrate the same
7+
behavior and results. It requires that they be built following the same pattern.
8+
9+
This way, it is possible to simply focus on the real problem that these applications
10+
are trying to solve and stop worrying about their layout.
11+
12+
## Which problem mikros try to solve
13+
14+
As stated earlier, the framework aims to remove all the complexity involved in
15+
building the structure of an application. Such as how and where to initialize
16+
dependent resources, structures for passing data, etc.
17+
18+
Mikros imposes a format for initializing an application where the developer
19+
determines its category and its main functionalities. From there, the pieces
20+
just need to be fitted together, whether it is the handling of an RPC call
21+
from a gRPC service or a task to be executed by an HTTP service when a
22+
certain endpoint is triggered.
23+
24+
## Usage
25+
26+
Mikros is a rust crate, i.e., to be used inside rust projects is just like any
27+
common crate. One must add it as a dependency in the Cargo.toml file and its
28+
API should be available to be used.
29+
30+
To work properly, the following steps can be used to write a new application
31+
with it:
32+
33+
- create a `service.toml` file defining the service kind of the application;
34+
- implement the trait related to the chosen service kind;
35+
- initialize the API using the service implementation.
36+
37+
Example: suppose you need an application that needs to run for an indefinite
38+
period of time, performing some type of task that cannot be stopped. For this,
39+
you can choose the **native** service kind to be used.
40+
41+
First, create a new cargo project for binary applications:
42+
43+
```bash
44+
cargo new native-service
45+
```
46+
47+
Then, inside the new service directory, create the service definitions file.
48+
This is how it should look like:
49+
50+
```toml
51+
name = "my-service-name"
52+
types = [ "native" ]
53+
version = "v0.1.0"
54+
language = "rust"
55+
product = "my-awesome-product"
56+
```
57+
58+
Adjust the required dependencies inside the project **Cargo.toml** file by
59+
adding the following:
60+
61+
- async-trait
62+
- mikros
63+
- tokio
64+
65+
Inside the **main.rs** file, implement the following:
66+
67+
- a structure that will implement the NativeService trait:
68+
69+
```rust
70+
#[derive(Clone, Default)]
71+
pub struct AppService;
72+
73+
#[async_trait::async_trait]
74+
impl mikros::service::native::NativeService for AppService {
75+
async fn start(&self, ctx: &mikros::service::context::Context) -> mikros::errors::Result<()> {
76+
// ctx is the mechanism to access mikros APIs and get the logger,
77+
// environment variables, service definitions and access to features.
78+
Ok(())
79+
}
80+
81+
async fn stop(&self, ctx: &Context) {
82+
// release some resource that the application is holding
83+
}
84+
}
85+
```
86+
87+
- if required to initialize some resource for the service, implement the Lifecycle
88+
trait:
89+
90+
```rust
91+
#[async_trait::async_trait]
92+
impl mikros::service::lifecycle::Lifecycle for AppService {
93+
async fn on_start(&mut self, ctx: &mikros::service::context::Context) -> mikros::errors::Result<()> {
94+
// again, ctx can help access the mikros API and use it for initialize
95+
// some custom internal service resource.
96+
Ok(())
97+
}
98+
99+
async fn on_finish(&self) -> mikros::errors::Result<()> {
100+
Ok(())
101+
}
102+
}
103+
```
104+
105+
- initialize the service using mikros API:
106+
107+
```rust
108+
#[tokio::main]
109+
async fn main() {
110+
let s = AppService::default();
111+
let svc = mikros::service::builder::ServiceBuilder::default()
112+
.native(Box::new(s))
113+
.build();
114+
115+
match svc {
116+
Ok(mut svc) => svc.start().await,
117+
Err(e) => panic!("{}", e.to_string()),
118+
}
119+
}
120+
```
121+
122+
The complete example can be found inside the [examples](../examples/apps/native) directory.
123+
124+
### Service definitions file
125+
126+
The service definitions file is a [TOML](https://toml.io/en/) file with information
127+
about the service.
128+
129+
Its objective is, in addition to defining some important information for executing
130+
the application, some other information to assist possible deployment or monitoring
131+
tools.
132+
133+
Its mandatory content is summarized in the following fields:
134+
135+
- name: a string to set the application name
136+
- types: a string array defining the service kind, which can be more than one,
137+
as long as they have the same mode of execution (Block/NonBlock)
138+
- version: the application version
139+
- language: the programming language of the application
140+
- product: the product name to which the application belongs
141+
142+
Optional fields:
143+
144+
- envs: a string array of required environment variables for the service.
145+
146+
Additionally, you can use this same file for the following types of definitions:
147+
148+
- features: a map of feature definitions, to set custom features settings for
149+
the application.
150+
- services: a map of service definitions, to set custom service kind settings
151+
for the application.
152+
- clients: a map of client connection definitions, for dependent applications.
153+
154+
Example:
155+
156+
```toml
157+
name = "my-service-name"
158+
types = [ "native" ]
159+
version = "v0.1.0"
160+
language = "rust"
161+
product = "my-awesome-product"
162+
envs = [ "CUSTOM_ENV_1" ]
163+
164+
[features.simple_api]
165+
enabled = true
166+
167+
[clients.grpc]
168+
host = "localhost"
169+
port = 7071
170+
171+
[services.cronjob]
172+
frequency = "daily"
173+
```
174+
175+
### Environment variables
176+
177+
Mikros has some environment variables that it uses to set custom information
178+
while the application is running. They are the following:
179+
180+
| Name | Description |
181+
|----------------------------|--------------------------------------------------------------------------------------------------------------------------|
182+
| MIKROS_SERVICE_DEPLOY | A string to set the current deployment server of the application, like (dev, stage, prod). Default: local |
183+
| MIKROS_TRACKER_HEADER_NAME | A header name where the track ID will be located in HTTP requests/responses (not implemented yet). Default: X-Request-ID |
184+
| MIKROS_COUPLED_NAMESPACE | The namespace where services/applications are running, to build the gRPC connection URL. Default: localhost |
185+
| MIKROS_COUPLED_PORT | The default port for dependent gRPC services. Default: 7070 |
186+
| MIKROS_GRPC_PORT | Default listening port for gRPC applications. Default: 7070 |
187+
| MIKROS_HTTP_PORT | Default listening port for HTTP applications. Default: 8080 |
188+
189+
### The service structure
190+
191+
Each service kind has its own trait that needs to be implemented in the application
192+
main structure and passed in the API initialization.
193+
194+
A gRPC application depends on the declared API in the protobuf file. So the
195+
application must implement it.
196+
197+
An HTTP application instead does not have a proper trait or API to implement. It
198+
depends on its endpoints, where each one should have a handler for it.
199+
200+
### Lifecycle
201+
202+
Lifecycle is a trait that an application can implement in its main structure to
203+
receive callbacks from the framework at specific execution points. Allowing it
204+
to handle them to execute custom tasks at these points. Like for example
205+
initialize connections with coupled gRPC services that it requires.
206+
207+
Its declaration is the following:
208+
209+
```rust
210+
#[async_trait::async_trait]
211+
pub trait Lifecycle: LifecycleClone + Send + Sync {
212+
async fn on_start(&mut self, _ctx: &mikros::service::context::Context) -> mikros::errors::Result<()> {
213+
Ok(())
214+
}
215+
216+
async fn on_finish(&self) -> merrors::Result<()> {
217+
Ok(())
218+
}
219+
}
220+
```
221+
222+
Notice that the trait already has default implementation, so the service can
223+
choose not to implement it.
224+
225+
### Extending features
226+
227+
Mikros does not provide any feature out-of-the-box, like cache, database and
228+
similar stuff. But it provides an API to allow implementing support for these
229+
and to access them using the same syntax inside applications.
230+
231+
The trait [Feature](../src/plugin/feature.rs) shows an API that a feature
232+
should implement to be handled inside by the framework and provided to the
233+
applications. It has methods with the following characteristics:
234+
235+
- provide the feature name and information to be registered while the application
236+
is initializing.
237+
- initialize itself and clean its resources.
238+
- a public API for applications to use it.
239+
240+
For an example of how to implement, register and use external features you can
241+
check the [features](../examples/features) examples directory.
242+
243+
### Extending service kind
244+
245+
As mentioned before, mikros provides some kind of services that it implements
246+
inside and allows building applications with them. But if a specific kind of
247+
application (or service) is required by some solution, it also provides an API
248+
to allow implementing and to allow new kind of applications being built.
249+
250+
The trait [Service](../src/plugin/service.rs) shows an API that a new service
251+
kind should implement to be supported by the framework, and, consequently, new
252+
applications.
253+
254+
This API requires that the implementation provides information about the new
255+
service type such as:
256+
257+
- its name and information to be registered while the application is initializing.
258+
- its execution mode (blocking or non-blocking).
259+
- and its implementation.
260+
261+
The [services](../examples/services) directory shows an example of how to create
262+
a new service kind and the [cronjob](../examples/apps/cronjob_service) service
263+
shows how to register it and use it.

docs/service_grpc.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# gRPC services
2+
3+
The gRPC service kind supported by the framework is the way to build applications
4+
capable of using the [gRPC](https://grpc.io) framework behind the scenes.
5+
6+
This kind of application requires that its API to be defined using a [protobuf](https://protobuf.dev)
7+
spec file.
8+
9+
Mikros uses [tonic](https://github.com/hyperium/tonic) rust gRPC implementation
10+
internally.
11+
12+
In order to create a new service of this kind, the following steps can be used
13+
to help:
14+
15+
- create the service protobuf file with its API.
16+
- inside the project directory, use [tonic_build](https://docs.rs/tonic-build/latest/tonic_build/)
17+
and with a **build.rs** source file, compile the protobuf to generate rust code
18+
from it.
19+
- implement the service by implementing its API in a structure and use mikros
20+
API to initialize the service.
21+
22+
The examples directory has the following examples using the same steps described:
23+
24+
* [grpc](../examples/apps/grpc): a gRPC service which implements its API.
25+
* [grpc with lifecycle](../examples/apps/grpc_with_lifecycle): a gRPC service
26+
that also implements the Lifecycle trait.

docs/service_http.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# HTTP services
2+
3+
An HTTP Service is a web-based service designed to define and handle HTTP
4+
endpoints and routes. These services use the [axum](https://docs.rs/axum/latest/axum/)
5+
web framework to build flexible and efficient APIs.
6+
7+
A mikros HTTP service requires that each router handler should have, at least,
8+
a [State](https://docs.rs/axum/latest/axum/#using-the-state-extractor) in its
9+
arguments. Like the following:
10+
11+
```rust
12+
use mikros::http::ServiceState;
13+
14+
async fn handler(State(state): State<Arc<Mutex<ServiceState>>>) -> String {
15+
// state will provide access to the framework context, and if chosen, to
16+
// a custom application internal state.
17+
format!("Handler")
18+
}
19+
```
20+
21+
The examples application directory contains different examples of how to implement
22+
an HTTP service using mikros.

docs/service_native.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Native services
2+
3+
A Native Service is a general-purpose application designed to execute any
4+
arbitrary code for extended periods. It does not rely on a defined API and
5+
is well-suited for tasks requiring flexible execution models.
6+
7+
Native services implement the [NativeService](../src/service/native/mod.rs)
8+
trait to conform to the expected lifecycle and operational structure.
9+
10+
This trait ensures the service adheres to a standard lifecycle and
11+
can be managed effectively within the broader application framework. It
12+
is composed of the following methods:
13+
14+
- start: the place where the application is initialized and the main task
15+
is executed.
16+
- stop: the place to finish everything that was initialized before.
17+
18+
For more details about how to implement a native service check the following
19+
[example](../examples/apps/native).

docs/service_script.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Script services
2+
3+
A script service is a general-purpose application designed for one-off tasks
4+
that execute a single function and then terminate. Unlike continuous services,
5+
script services are concise, focusing on completing a specific operation and
6+
performing any necessary cleanup afterward.
7+
8+
To be a script service it must implement the [ScriptService](../src/service/script/mod.rs)
9+
trait to standardize their lifecycle behavior.
10+
11+
For more details about how to implement a script service check the following
12+
[example](../examples/apps/script).

examples/features/example/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,6 @@ impl ExampleAPI for Example {
5959
}
6060
}
6161

62+
// Use this macro to export a public API equal for all features, allowing the
63+
// services to use the same syntax while accessing them.
6264
impl_feature_public_api!(ExampleAPI, Example, "example");

examples/features/simple_api/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,6 @@ impl ExampleAPI for Example {
7979
}
8080
}
8181

82+
// Use this macro to export a public API equal for all features, allowing the
83+
// services to use the same syntax while accessing them.
8284
impl_feature_public_api!(ExampleAPI, Example, "simple_api");

0 commit comments

Comments
 (0)