Тестовое задание для MediaLand.
Клонировать проект:
git clone https://github.com/Amegatron/medialand-test.git
cd medialand-test
docker-compose up
Веб-сервис будет доступен по адресу http://localhost:8080
Если Docker недоступен, требуется вручную настроить веб-сервер.
В случае NGINX-а добавить конфиг nginx/conf.d/uuids.conf
к конфигам NGINX-а, заменив в нем следующее:
- В строке
listen 80 default_server;
убратьdefault_server
и по желанию заменить порт на более удобный. - Путь в строке
root /var/www/public;
заменить на фактический путь, по которому лежит директорияpublic
проекта. - В строке
fastcgi_pass ...;
заменитьcounters-app:9000
в соотв-ии с локальной конфигурацией PHP-FPM.
Класс Counter
представляет собой основную сущность с точки зрения бизнес-логики. Основная проблема со счетчиком заключается в соблюдении его атомарности.
Для решения этой проблемы работа с этим классом должна происходить по принципу Синглтона, чтобы в рамках одного рабочего процесса один и тот же счетчик с точки зрения БЛ (счетчики с одним и тем же UUID) был представлен единственным экземпляром.
По части управления различными экземплярами счетчиков класс Counter
опирается на предоставляемого менеджера счетчиков (интерфейс CounterManagerInterface
).
Атомарность также должна достигаться при параллельных запросах к приложению.
В силу особенностей PHP, Counter
сам по себе не имеет возможности контролировать это, и подобная атомарность может быть обеспечена только внешним по отношению к данному классу слоем. Поэтому Counter
делегирует задачу увеличения значения счетчика внешним инкременторам посредством интерфейса IncrementorInterface
.
Данный слой является управляющим для слоя БЛ и также обеспечивает связь между Counter
и слоем хранения, который, в свою очередь, также обеспечивает атомарные изменения счетчика.
Класс CountManager
является конкретной реализацией интерфейса CounterManagerInterface
, обеспечивая работу со счетчиками в Синглтон-манере, для чего использует SingletonManagerInterface
и соответствующую реализацию.
Для реализации хранения CountManager
работает с различными драйверами (CounterPersistenceDriverInterface
), которые берут на себя роль как непосредственно хранения счетчиков, так и их атомарного изменения.
Данный слой содержит различные драйверы для хранения и изменения счетчиков. На данный момент представлена одна реализация, обеспечивающая работу этих механизмов за счет файловой системы (FileSystemPersistenceDriver
).
Драйверы также предоставляют соответствующие инкременторы, которыми пользуются счетчики для инкремента.
Данный слой предоставляет функционал для общих используемых действий в системе независимо от вариантов использования самой системы (Веб/Консоль/...). А именно: создать или увеличить счетчик, а также просто получить существующий счетчик.
Данный слой реализован в виде шины команд (Command Bus
), где каждое действие в системе представлено отдельной командой.
Шина реализована пакетом league/tactician
.
Данный слой отвечает исключительно за взаимодействие с приложением через Web: принимает HTTP-запросы, выполняет существующие команды и возвращает ответы. Для реализации взаимодействия с Web используются PSR-совместимые реализации для запросов и ответов:
laminas/laminas-diactoros
- имплементация PSR-7laminas/laminas-httphandlerrunner
- эмиттер ответовleague/route
- маршрутизатор
К сожалению, на полноценные тесты всего приложения времени не хватило. Но для простой проверки на надежность инкремента при большом кол-ве одновременных запросов создал отдельный скрипт на Go, расположенный здесь: https://gist.github.com/Amegatron/2c30a687b6468a52d7cb1d3eb5f3aa73
Скрипт производит множество параллельных запросов на инкремент счетчика и проверяет целостность данных.
Пример запуска:
medialand-test.go 100 http://localhost:8080/v1/counters/897e4dcb-68bc-46b2-87c4-758d77c00ee9
Где 100
- кол-во одновременных POST запросов на указанный URL
.