Есть несколько корпоративных приложений. Каждое из приложений написано на на PHP7 в своем стиле, в своих изолированных репозиториях, за преложения ответсвенны разные разработчики. Каждое приложение имеет своих пользователей. Любой пользователь может подключить различные виды связи с ним. Есть некие процессы в приложении, после которых нужно уведомить пользователя по всем зарегистрированным каналам связи.
В приложении A можно ввести только свой электронный адрес и номер телефона. Пользователь Bob ввел в систему свой электронный адрес bob@example.com и свой номер телефона +375555555555. После логина Bob в систему, приложение посылает сообщение "Hi" пользователю Bob на Email и SMS.
В приложении B можно войти с помощью социальных сетей, email, мобильного номера телефона. Пользователь Alice зашла с мобильного iOS, разрешила Push уведомления (приложению доступен APNS token) и ввела свой email. Для Alice строка "Hello" должна быть доставлена через Push уведомление на ее мобильный телеофн и в сообщении на электронную почту.
Требуется реализовать библиотеку (не сервис), для рассылки строки на все доступные транспорты пользователя. Библиотека будет использована разными приложениями, поэтому ничего "не должна знать" о этих приложениях.
Этой библиотекой будут пользоваться другие разработчики. Разработчики будут ее подключать (копировать исходники или устанавливать через composer), настраивать (через контейнер зависимости или непосредственно по месту использования) и рассылать строку.
Библиотека должна позволять любому приложению добавить свою реализацию транспорта. Релазиция низкоуровнего транспорта в этом ТЗ нам не интересна. Договоримся, что низкоуровневый транспорт всегда выполняется (не нужна обработка ошибок) и выглядит так:
function emailNativeSend(string $message, string $emailAddress, string $smtpUrl, string $smtpUser, string $smptPassword): void;
function smsNativeSend(string $message, string $phoneNumber, string $serviceSecret): void;
function apnsPushNativeSend(string $message, int $deviceId, string $apnsUrl, string $apnsSecretKey): void;
function gsmPushNativeSend(string $message, int $deviceId, string $gsmUrl, string $gsmSecretKey): void;
//etc
Использование библиотеки должно быть максимально простым и принимать только два аргумента - идентификатор пользователя и сообщение для передачи.
- PHP 7.1+, phpunit
- Продуманная OOP архитектура по принципам GRASP, SOLID, DDD
- Минимум один unit тест на логику библиотеки
- Минимум два транспорта в стандартной поставке библиотеки
- Библиотека должна иметь возможность расширять доступные транспорты
- Предоставить пример (Readme.md - можно на русском) как библиотеку настраивать и использовать
Рассмотрим пример, где наc, в принципе, устраиват функциональность (не хватае расширяемости со стороны приложения) но категорически (warning) не устраивает все остальное:
<?php
// библиотека
class TheSender
{
public static $isEmailAvailable = false;
public static $smtpAddress = "";
public static $smtpUser = "";
public static $smtpPassword = "";
public static $isSmsAvailable = false;
public static $smsSecretKey = "";
public static $knownUsers = [];
public function send(string $id, string $message)
{
if (isset(static::$knownUsers[$id]['email']) && static::$isEmailAvailable) {
emailNativeSend(
$message,
static::$knownUsers[$id]['email'],
static::$smtpAddress,
static::$smtpUser,
static::$smtpPassword
);
}
if (isset(static::$knownUsers[$id]['smsNumber']) && static::$isSmsAvailable) {
smsNativeSend($message, static::$knownUsers[$id]['smsNumber'], static::$smsSecretKey);
}
}
}
// настройка
TheSender::$isEmailAvailable = true;
TheSender::$smtpAddress = 'smtp.example.com';
TheSender::$smtpUser = 'login';
TheSender::$smtpPassword = 'password';
// использование
TheSender::$knownUsers[] = [
$user->getId() => [
'email' => $user->getEmail(),
'smsNumber' => $user->getPhone(),
],
];
TheSender::send($user->getId, 'Hi!');