Hola a todos, mi nombre es Alex. Quiero presentarles mi marco PHP para crear microservicios. Surgió de mi experimento de hace tres años, que luego se convirtió en un proyecto favorito, y más tarde en este marco creé varios proyectos de producción.
Cuando comencé a hacerlo, me propuse tomar una decisión que:
- se puede integrar fácilmente en proyectos existentes;
- puedes crear rápidamente al menos algo que funcione;
- los diseños más concisos y expresivos;
- Utilizó sabiamente las capacidades del PHP moderno.
Entonces, ¿por dónde empiezas? Por supuesto de la fuente! Puedes verlos en
githubBueno, para no entrar en largos argumentos, comencemos de inmediato con un ejemplo de trabajo.
En primer lugar, necesitamos .htaccess, en el que configuraremos varias reglas:
# 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]
Entonces puedes crear tu primer servicio. En él, crearemos un punto final que procesará el método GET y devolverá un mensaje de que todo está bien con él. Una especie de chequeo de salud.
Para comenzar, necesitamos conectar nuestro marco:
require_once ('vendor/service/service.php');
Luego creamos una clase para microservicio:
class TodoService extends ServiceBase implements ServiceBaseLogicInterface { }
Aquí tenemos:
- ServiceBase es una clase de servicio base con la funcionalidad más básica y más utilitaria;
- ServiceBaseLogicInterface es la interfaz que cualquier clase necesita implementar si desea proporcionar controladores de punto final. Si bien esta interfaz no impone ningún requisito especial en su clase. Solo hecho para escribir más estricto.
Luego obtenemos el primer controlador de punto final:
public function action_ping() { return ('I am alive!'); }
Luego lanzamos nuestro primer microservicio:
Service::start('TodoService');
Poniendo todo junto, obtenemos:
class TodoService extends ServiceBase implements ServiceBaseLogicInterface { public function action_ping() { return ('I am alive!'); } } Service::start('TodoService');
Puede surgir una pregunta razonable, ¿y en qué URL está disponible esta funcionalidad? El hecho es que al definir un método con el prefijo action_ <nombre-parte>, dejó en claro al servicio que es un controlador de URL <nombre-parte>, es decir en nuestro caso será algo así como
localhost / pingEl guión bajo en el nombre del método cambia a -. Es decir El método action_hello_world estará disponible en
localhost / hello-worldSeguimos conduciendo.
Al igual que para las aplicaciones GUI, sería bueno usar MVC (u otro patrón con separación del componente visual y la lógica), así como en el microservicio. Las cosas que se pueden destrozar se rompen mejor.
Es decir en nuestro caso, deje que la clase de servicio continúe realizando funciones utilitarias para inicializar el servicio e iniciar el controlador deseado, y poner la lógica en una clase separada. Para hacer esto, modificamos nuestro código de la siguiente manera:
class TodoLogic extends ServiceBaseLogic { public function action_ping() { return ('I am alive!'); } } class TodoService extends ServiceBase { } Service::start('TodoService', 'TodoLogic');
Luego tenemos una clase con lógica:
class TodoLogic extends ServiceBaseLogic
Heredado de la clase base ServiceBaseLogic (tiene un mínimo de funciones, por lo que lo examinaremos en detalle más adelante).
La clase TodoService ha dejado de implementar la interfaz ServiceBaseLogicInterface (de hecho, no ha desaparecido, justo ahora la clase ServiceBaseLogic la implementa).
Después de eliminar la lógica, la clase TodoService resultó estar vacía y se puede cortar de forma segura, reduciendo aún más el código:
class TodoLogic extends ServiceBaseLogic { public function action_ping() { return ('I am alive!'); } } Service::start('ServiceBase', 'TodoLogic');
Aquí, la clase ServiceBase es responsable de iniciar el servicio, no el nuestro.
Condujimos aún más lejos.
En el proceso de usar mi marco, en cierto momento, las clases con lógica de tamaño monstruoso comenzaron a resultar. Lo que irritaba mi sentido de la belleza, por un lado, por otro lado, Sonar estaba indignado, por otro lado, el concepto de dividir los métodos en métodos de lectura y escritura (ver CQRS) no estaba claro cómo implementarlo.
Por lo tanto, en un momento determinado, se hizo posible agrupar los controladores de punto final de acuerdo con uno u otro atributo de acuerdo con diferentes clases de lógica y, si es necesario, ejecutarlos dentro del mismo servicio o distribuirlos sin problemas en diferentes.
Es decir Puede hacer toda la lógica CRUD dentro de un servicio. Y se puede dividir en dos servicios:
- uno proporciona métodos de lectura;
- y el otro proporciona métodos de modificación de datos.
Agreguemos ahora un método de creación de entidad y un método de recuperación de lista de entidades a nuestro ejemplo:
class TodoSystemLogic extends ServiceBaseLogic { public function action_ping() { return ('I am alive!'); } } class TodoReadLogic extends ServiceBaseLogic { public function action_list() { return ('List!'); } } class TodoWriteLogic extends ServiceBaseLogic { public function action_create() { return ('Done!'); } } Service::start('ServiceBase', [ 'TodoSystemLogic', 'TodoReadLogic', 'TodoWriteLogic' ]);
Solo tocaremos los cambios:
- aparecieron las clases TodoSystemLogic (métodos del sistema), TodoReadLogic (métodos de lectura), TodoWriteLogic (métodos de escritura);
- Al iniciar el servicio, transferimos no una clase con lógica, sino varias.
Eso es todo por hoy. Discutiré otras características del marco en los siguientes artículos. Hay muchos de ellos. Mientras tanto, puedes ver por ti mismo
lo que es interesante allí .