DEV Community

loading...
Cover image for New PHP framework for creating microservices

New PHP framework for creating microservices

alexdodonov profile image alexdodonov Updated on ・4 min read

Hello everybody, my name is Alex. And I want to present you my PHP framework for creating micro services. It is based on my experiments in this area, then it has become a pet project, and then I have created several projects, wich are based on this framework.

When I have started developing it, I what to make solution wich:

  • can be easily used in the existing projects and legacy code;
  • have ability to create simple things fast;
  • be neat and expressive;
  • use abilities of the modern PHP.

What will be the first step? Sources of course ) It can be found on github

And to start fast lets create the first hello world application.

First of all, we need to understand how our endpoints will be called.

If you are using Apache, then you can create .htaccess file, with the following content:

# use mod_rewrite for pretty URL support
RewriteEngine on
RewriteRule ^([a-z0-9A-Z_\/\.\-\@%\ :,]+)/?(.*)$ index.php?r=$1&%{QUERY_STRING} [L]
RewriteRule ^/?(.*)$ index.php?r=index&%{QUERY_STRING} [L]

It will allow you to call endpoints like this https://localhost/hello-world/

But if you are using nginx or don’t want to use .htaccess then you can call endpoints in this way: https://localhost/?r=hello-world

And now we’re ready to create our first micro service. It will consists of one endpoint, wich will handle GET requests and return information that he is OK ) A sort of health check.

First of all, we need to add our framework to the project:

composer require mezon/mezon
Enter fullscreen mode Exit fullscreen mode

Then include autoload.php

require_once (autoload.php');
Enter fullscreen mode Exit fullscreen mode

And the first class will look like this:

class TodoService extends \Mezon\Service\ServiceBase implements \Mezon\Service\ServiceBaseLogicInterface
{ /* class body */ }
Enter fullscreen mode Exit fullscreen mode

More details about it:

ServiceBase – base class for all micro services with the most common functionality;

ServiceBaseLogicInterface – this interface must be implemented by class if it contains endpoint handlers. It does not yet force you to implement methods, it is just made for more strict typization.

And now we are ready to implement the first endpoint handler:

public function actionPing()
{
return ('I am alive!');
}
Enter fullscreen mode Exit fullscreen mode

And then launch it:

\Mezon\Service\Service::start(TodoService::class);
Enter fullscreen mode Exit fullscreen mode

Let’s combine all lines of code and look at the whole picture:

/**
 * Service class
 */
class TodoService extends \Mezon\Service\ServiceBase implements \Mezon\Service\ServiceBaseLogicInterface
{
    /**
     * First endpoint
     */
    public function actionPing()
    {
        return ('I am alive!');
    }
}

\Mezon\Service\Service::start(TodoService::class);
Enter fullscreen mode Exit fullscreen mode

You may want to ask me – wich URL should we call? The truth is that if you have method with the name action it means that framework will automatically create handler for the URL In our case it will look like this https://localhost/ping/

By the way: capital letters will be replaced on lower case letters prefixed with ‘-’. For example the method actionHelloWorld will become handler for the next URL: https://localhost/hello-world/

Let’s dig deeper.

All of you know good practices for applilcations. For example, MVC (or any other pattern of the same kind). We have the same story in the world of micro services. I mean that all things wich must be isolated better keep isolated.

In our case service’s class should do one thing – combine parts of this puzzle and logic should be in another class. To do this let’s modify our code as shown below:

class TodoLogic extends \Mezon\Service\ServiceBaseLogic
{

    /**
     * First endpoint
     */
    public function actionPing()
    {
        return ('I am alive!');
    }
}

class TodoService extends \Mezon\Service\ServiceBase
{
}

\Mezon\Service\Service::start(TodoService::class, TodoLogic::class);
Enter fullscreen mode Exit fullscreen mode

You may notice that we have created separate class with our logic:

class TodoLogic extends \Mezon\Service\ServiceBaseLogic
Enter fullscreen mode Exit fullscreen mode

It is derived from the class ServiceBaseLogic (it provides some functions wich will be described further).

The class TodoService is no longer implements the interface ServiceBaseLogicInterface, but now the class ServiceBaseLogic implements it.

After the logic was excluded from the service class and moved to the logic class we have got quite empty class. And it can be completely removed:

class TodoLogic extends \Mezon\Service\ServiceBaseLogic
{

    /**
     * First endpoint
     */
    public function actionPing()
    {
        return ('I am alive!');
    }
}

\Mezon\Service\Service::start(\Mezon\Service\ServiceBase::class, TodoLogic::class);
Enter fullscreen mode Exit fullscreen mode

In this example the service is launched by default class ServiceBase, not our custom one.

Let’s dig even more deeper.

After I have used this framework in several projects I have got quite huge classes with tons of busyness logic. From one side it was hurting my eyes, and from the other side Sonar Cube have raised lots of errors, and finally it was not clear how implement strategy of separating read- and write methods (i.e. CQRS).

That’s why I have implemented the feature wich allows group handlers within separate classes with logic. And it allows to use them within on service or in separate ones.

For example, you can implement the whole CRUD logic in one service. But you can also split methods in two groups:

  • group of methods for reading data (R in CRUD);
  • and group of methods for changing data (CUD in CRUD).

And as an illustration let’s add several new methods:

class TodoSystemLogic extends (\Mezon\Service\ServiceBaseLogic
{
    public function actionPing()
    {
        return ('I am alive!');
    }
}

/**
 * Read logic implementation
 */
class TodoReadLogic extends (\Mezon\Service\ServiceBaseLogic
{
    public function actionList()
    {
        return ('List!');
    }
}

/**
 * Write logic implementation
 */
class TodoWriteLogic extends (\Mezon\Service\ServiceBaseLogic
{
    public function actionCreate()
    {
        return ('Done!');
    }
}

\Mezon\Service\Service::start(\Mezon\Service\ServiceBase::class, [
    TodoSystemLogic::class,
    TodoReadLogic::class,
    TodoWriteLogic::class
]);
Enter fullscreen mode Exit fullscreen mode

Let’a review main changes:

  • we have created classes with logic TodoSystemLogic (system methods), TodoReadLogic (read methods), TodoWriteLogic (data change methods);
  • when the service is launched we pass several classes with logics as parameters, not one like in the previous examples.

Well that’s all for today. Other abilities of the framework will be described in the next article. There are a lot of thigs to be shown. And for now visit repository of the project

It will be great if you will contribute something to this project. Documentation, sharing the project in your social media, bug fixing, refactoring, or even submitting issue with question or feature request. Thanks anyway )

Learn more

More information can be found here:

Twitter

Medium

Slack

Discussion (6)

Collapse
w3gaucho profile image
W3G • Edited

your router is very similar to Klein, why don't you use it? have you ever thought about making an automatic routing REST system?

Collapse
w3gaucho profile image
W3G

for sample, in Laravel 5.4 you can auto routing using the "resource" method. in my conception of a real "automatic REST routing" is possible do the same without use the "resource" method.

Collapse
alexdodonov profile image
alexdodonov Author

I shall dig into it. Stay tuned )

Collapse
alexdodonov profile image
Collapse
alexdodonov profile image
alexdodonov Author

What do you mean "automatic REST routing"?

Collapse
alexdodonov profile image
alexdodonov Author

Any comments people? )

Forem Open with the Forem app