Introduction to MoonLight Architecture

MoonLight is a rather new architecture that is based on the commonly used architectures of gRPC, MVC(Model View Controller), Micro-Services and Monolothic. It picks the best of all these architectures and combines them to create a new architecture that is more efficient and scalable.

The MoonLight paradigm

Below are the new conventions that MoonLight architecture brings to the table:

Architecture Overview

Moonlight Architecture Overview

1. Single API endpoint/route

In the moonlight, all requests target the same endpoint. This is to ensure that the application is scalable and easy to maintain. This also makes it easier to debug and monitor the application.

Assuming the application is running on http://localhost:3000, all requests will be made to http://localhost:3000/api/v1/. This is the only endpoint that is exposed to the outside world.

On top of other advantages, now frontend devs don’t have to worry about the base URL of the API. They can just make requests to the /api/v1/ endpoint and the application will handle the rest.

Point To Ponder!

For anyone to interact with the application, they must go through this endpoint only.

2. POST Requests only

All requests under moonlight architecture are made using the http method of POST only. This is to ensure that the requests are secure and the data is not exposed in the server logs or in the URL. This also makes it easier to debug and monitor the application. Read more on security.

3. Single Request Format

In Moonlight architecture, all requests are made in a similar format. This makes it easier to understand and debug the requests. Requests can either be of type JSON or form-data.

Every request must define the SERVICE and ACTION to execute in the request body plus the rest of the payload as required by the service.

POST http://localhost:3000/api/v1/
{
  "SERVICE": "users",
  "ACTION": "get_user_by_profile",
  "profile": "@1233232"
}

The SERVICE and ACTION are required in every request. The rest of the payload is dependent on the service and action being executed.

Point To Ponder!

This architecture, if to be well implemented must follow the Object Oriented Programming paradigm. With this, services should/must be classes or interfaces(golang) that combine together related business logic like AuthenticationService, ProductService, OrderService etc.

And Actions should be methods in these classes like login, register in the AuthenticationService class.

4. Single Response Format

This architecture also calls for a single response format. This makes it way easier to understand and debug the responses. The response format is as follows:

Response
{
  "statusCode": 0,
  "returnMessage": "Some cool message here or null",
  "returnData": "the data you're sending to the frontend",
  "extraData": "any extra data you want to send to the frontend"
}

With statusCode, it implies that the developer/business can define their own custom status codes. However, by convention, a status code of 0 implies success and is recommended to be kept for the same.

5. HTTP 200 OK for all.

All requests in this architecture that reach the application server should/must return an http status of 200 OK. The only special case is 502 Gateway Error which is returned when the application server is down or unreachable.

Point To Ponder!

Think about it, the application server actually handled your request so whether the request raised an exception or successfully executed, the server actually handled it. And that’s what we are actually looking for.

6. Single Controller Per Application.

In this architecture, there is only one controller that handles all the requests. This controller is responsible for routing the requests to the appropriate service and action. This makes it easier to maintain and debug the application. This must be one per application.

To achieve api versioning, this controller can have multiple actions each pointing to a different version switch of the application. Each action can then route the request to the appropriate service switch.

This point is where the app should handle all exceptions that might be raised by the services and then return a 200 OK response with the appropriate message from the exception raised.

Example using php.

Controller
  class ApiController extends Controller
  {
      public function v1()
      {
          try {
              // point to the service switch for version one
          } catch (\Exception $e) {
              // handle the exception here
          }
      }

      public function v2()
      {
          try {
              // point to the service switch for version two
          } catch (\Exception $e) {
              // handle the exception here
          }
      }
  }
<?php

No logic should be in the controller; it should only be responsible for routing the requests to the appropriate switch.

For Pionia Framework, this is not required at all, since the framework handles this for you internally!

7. Single Service Switch Per API Version.

In this architecture, there should only be one service switch that handles all the requests for a particular version of the application. This service switch is responsible reading the SERVICE in the request and call the responsible service passing it the action(ACTION) and the rest of the payload.

The service switch is just a convention to make our controller clean and easy to maintain. It is not a must to have it, but it is recommended. Otherwise, the switching logic would be handled in the controller action responsible for the version.

Point To Ponder!

The controller focuses on mapping the requests, and the switch focuses on mapping the services and actions.

8. Services and Actions

In MoonLight architecture, services are classes or interfaces that combine related business logic. For example, AuthenticationService, ProductService, OrderService etc.

Actions are methods in these classes, like login, register in the AuthenticationService class.

9. Database and Querying

This is a good to have but not enforced by the architecture. In moonlight architecture, querying the database is highly recommended over using an ORM. This is because querying the database directly is faster and more efficient than using an ORM. Also, querying the database directly gives the developer more control over the queries and the data being returned.

All ORMs used in Moonlight should not map result sets to models. They should return the result set as is.

This removes the unnecessary overhead of mapping the resultset to models(model hydration) and makes the application faster and more efficient.

10. Separation of Concerns

In this architecture, the backend is meant to support, command and act as a single source of truth for the frontend. This implies that the architecture requires implementers to implement the frontend and backend separately.

However, this does not discourage serving the two together in production. Frameworks like Spring boot give you the ability to serve your entire frontend under your backend. This is highly encouraged. But Moonlight discourages writing of Frontend logic using the backend specific tools like PHP, Python and others.

Developers should solely harness the existence of the frontend tools to write re-usable, modularised and maintainable frontend code.

This way every tool will be maximised to its full potential.

Advantages of MoonLight Architecture
  1. Scalability:- Since adding new services and actions is easy, the application can be scaled easily.
  2. Maintainability:- Small codebase and single-logic services make it easy to maintain the application. Even new developers can easily understand the codebase.
  3. Security:- Since all requests are made using POST, the data is secure and not exposed in the web server logs. Also, action-level authentication and authorisation make it easy to secure the application at lower levels.
  4. High Performance:- Architecture stresses on querying the database directly and not using ORMs. This makes the application faster and more efficient.
  5. Developer Performance:- Since the architecture is simple and easy to understand, developers can roll out new features quickly without having to write a lot of boilerplate code.
  6. Easy Debugging:- Since all requests and responses are made in a similar format, it is easy to debug the application. Also, the single endpoint makes it easy to monitor the application.
  7. Frontend Integration:- One endpoint for all requests, one request format, one response format makes it easy for frontend devs to integrate the api.
Disadvantages of MoonLight Architecture
  1. Some languages may not support switching between form-data and JSON requests. This might dictate all uploads to be base64 encoded.
  2. Moonlight is only suitable for APIs and not for full-stack applications.
  3. The architecture being new, there might be a learning curve for new developers and a small community to get help from.

The community is growing, and we are looking forward to having you on board.