Arquitectura orientada a Servicios

Gaphor tiene una arquitectura orientada a servicios. ¿Qué significa esto? Bueno, Gaphor está construido como un conjunto de pequeñas islas (servicios). Cada isla proporciona una funcionalidad específica. Por ejemplo, utilizamos servicios separados para cargar/guardar modelos, proporcionar la estructura del menú, y para manejar el sistema de deshacer.

Definimos los servicios como puntos de entrada en el pyproject.toml. Con los puntos de entrada, las aplicaciones pueden registrar funcionalidades para fines específicos. También agrupamos los puntos de entrada en grupos de puntos de entrada. Por ejemplo, utilizamos el grupo de puntos de entrada console_scripts para iniciar una aplicación desde la línea de comandos.

Servicios

Gaphor se modela en torno al concepto de servicios. Cada servicio puede ser registrado con la aplicación y luego puede ser utilizado por otros servicios u otros objetos que existen dentro de la aplicación.

Cada servicio debe implementar la interfaz Service. Esta interfaz define un método:

shutdown(self)

Que se llama cuando hay que limpiar un servicio.

Permitimos que cada servicio defina sus propios métodos, siempre y cuando el servicio también esté implementado.

Los servicios deben definirse como puntos de entrada en el archivo pyproject.toml.

Normalmente, un servicio realiza algún trabajo en segundo plano. Los servicios también pueden exponer acciones que pueden ser invocadas por los usuarios. Por ejemplo, la combinación de teclas Ctrl-z (deshacer) está implementada por el servicio UndoManager.

Un servicio también puede depender de otros servicios. La inicialización del servicio resuelve estas dependencias. Para definir una dependencia de servicio, basta con añadirla al constructor por su nombre definido en el punto de entrada:

class MyService(Service):

    def __init__(self, event_manager, element_factory):
        self.event_manager = event_manager
        self.element_factory = element_factory
        event_manager.subscribe(self._element_changed)

    def shutdown(self):
        self.event_manager.unsubscribe(self._element_changed)

    @event_handler(ElementChanged)
    def _element_changed(self, event):

Los servicios que exponen acciones también deben heredar de la interfaz ActionProvider. Esta interfaz no requiere la implementación de ningún método adicional. Los métodos de acción deben llevar la anotación @action.

Ejemplo: fábrica de elementos

A nice example of a service in use is the ElementFactory. It is one of the core services.

When an important events occurs, like an element is created or destroyed, that event is emitted. We then use an event handler for ElementFactory that stores the add/remove signals in the undo system. Another example of events that are emitted are with Element’s. Those classes, or more specifically, the properties, send notifications every time their state changes.

Puntos de entrada

Gaphor uses a main :refentry point <https://packaging.python.org/en/latest/specifications/entry-points/> group called gaphor.services.

Los servicios se utilizan para llevar a cabo la funcionalidad básica de la aplicación, dividiendo las funciones en componentes individuales. Por ejemplo, la fábrica de elementos y el gestor de deshacer son ambos servicios.

Plugins pueden ser creados también para extender Gaphor más allá de la funcionalidad de núcleo como un add-on. Por ejemplo, un plugin podría ser creado para conectar los datos del modelo a otras aplicaciones. Los plugins también son definidos como servicios. Por ejemplo, un nuevo plugin XMI de exportación sería definido como sigue en el pyproject.toml:

[tool.poetry.plugins."gaphor.services"]
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"

Interfaces

Cada servicio (y plugin) debe implementar la interfaz gaphor.abc.Service:

class gaphor.abc.Service[fuente]

Interfaz base para todos los servicios de Gaphor.

abstractmethod shutdown() None[fuente]

Apagar los servicios, liberar recursos.

Otro servicio más especializado que también hereda de gaphor.abc.Service, es el servicio UI Component. Los servicios que utilizan esta interfaz se utilizan para definir las ventanas y la funcionalidad de la interfaz de usuario. Un componente de interfaz de usuario debe implementar la interfaz gaphor.ui.abc.UIComponent:

class gaphor.ui.abc.UIComponent[fuente]

Un componente de la interfaz de usuario.

abstractmethod close()[fuente]

Cierre el componente de la interfaz de usuario.

El componente puede decidir ocultar o destruir los componentes de la interfaz de usuario.

abstractmethod open()[fuente]

Crear y mostrar los componentes de la interfaz de usuario (ventanas).

shutdown()[fuente]

Apague este componente.

No se supone que se vuelva a abrir.

Typically, a service and UI component would like to present some actions to the user, by means of menu entries. Every service and UI component can advertise actions by implementing the gaphor.abc.ActionProvider interface:

class gaphor.abc.ActionProvider[fuente]

Un proveedor de acciones es un servicio especial que proporciona acciones a través de decoradores de @action en sus métodos (ver gaphor/action.py).