Я начинаю изучать ООП, и я знаю, что трудно создать хороший, качественный, тестируемый код, и я боюсь сделать какую-то архитектурную ошибку и начать, потому что потом труднее провести рефакторинг.
В настоящее время я работаю над Laravel, и мне нужен компонент (небольшая часть программы) для вычисления статистики онлайн-рекламы (CPM, EPC и т. Д.) В cronjob.Для этого мне нужно собрать данные из базы данных, рассчитать статистику (и) и сохранить их в связанной таблице.Это нужно запустить через CLI с помощью cronjob.Расчет статистики должен выполняться, если это возможно, с помощью SQL, но это не всегда может быть сделано с текущей архитектурой.Поэтому мне нужно создать какой-то повторно используемый компонент, который можно легко расширить с помощью новой логики расчета статистики, либо просто извлекая уже рассчитанную логику из БД и сохраняя ее, либо извлекайте, вычисляйте и сохраняйте в БД.И чтобы у futuhre была возможность легко использовать его в любой части приложения, а не только в CLI.
Для запуска из CLI я использую команду Laravel с расписанием:
class StatsComamnd extends Command
{
protected $signature = 'project:calculatestats {statName}';
public function __construct(StatsService $statsService){
parent::__construct();
$this->statsService = $statsService;
}
public function handle() {
$method = $this->argument('statName');
if(!method_exists($this, $method)) {
$this->error('Invalid stat name provided!');
}
$this->{$method}();
}
public function networkOffers():void {
$this->stastService->setStatsHandler(app(OffersNetworkStatsHandler::class))->handle();
}
public function networkOffersCpm():void{
app(OffersNetworkCpmHandler::class)->handle();
}
public function networkOffersEpc:void{
app(OffersNetworkEpcHandler::class)->handle();
}
public function networkSurveys():void{
app(SurveysNetworkHandler::class)->handle();
}
public function networkSurveysCpm():void{
app(SurveysNetrworkCpmHandler::class)->handle();
}
public function networkSurveysEpc:void{
app(SurveysNetworkEpcHandler::class)->handle();
}
//...other handlers, like countryOffersCpm, toolSpecificOffersCpm and so on
}
SurveysNetrworkCpmStatsHandler:
/** This handle responsible of collectiong, calculating and storing network wide survey CPMs. We can't calculate CPM inside DB, so here we are going to use CpmCalculator */
class SurveysNetrworkCpmStatsHandler implements StatsHandlerInterface {
private $surveyImpressionsRepo;
private $statsRepo;
private $vcPointRepo;
private $calculator;
public function __construct(
SurveyImpressionRepositoryInterface $surveyImpressionRepository,
SurveyStatsRepositoryInterface $statsRepository,
VcPointRepositoryInterface $vcPointRepository,
CpmCalculator $calculator
){
$this->surveyImpressionsRepo = $surveyImpressionRepository;
$this->calculator = $calculator;
$this->vcPointRepo = $vcPointRepository;
$this->statsRepo = $statsRepository;
}
public function handle() {
$stats = [];
list($impressions, $conversions) = $this->fetchStatisticData();
foreach ($impressions as $impression) {
$sid = $impression->survey_id;
$conversion = $conversions->first(function($conversion) use ($sid) {
return $conversion->survey_id === $sid;
});
if(!isset($conversion)) {
continue;
}
$stat = new \SurveyNetworkCpmStat();
$stat->offer_id = $impression->offer_id;
$stat->survey_id = $sid;
$stat->mobile_cpm = $this->calculator->setConversionCount($conversion->conversions_count_mobile)->setImpressionsCount($impression->unique_impressions_count_mobile)->setPayoutSum($conversion->payout_sum_mobile)->calculate();
$stat->desktop_cpm = $this->calculator->setConversionCount($conversion->conversions_count_desktop)->setImpressionsCount($impression->unique_impressions_count_desktop)->setPayoutSum($conversion->payout_sum_desktop)->calculate();
$stat[] = $stat->toArray();
}
$this->store($stats)
}
private function fetchStatisticData(){
$impressions = $this->surveyImpressionsRepo->getImpressionsForNetworkCpm();
$conversions = $this->vcPointRepo->getConversionsForSurveyNetworkCpm();
return [$impressions, $conversions];
}
private function store($stst): bool{
$this->statsRepo->insert()
}
}
SurveysNetrworkStatsHandler:
/** This handle responsible of collectiong, calculating and storing all network wide survey stats.*/
class SurveysNetrworkStatsHandler implements StatsHandlerInterface {
private $cpmHandler;
private $epcHandler;
private $statsRepo;
public function __construct(
SurveysNetrworkCpmStatsHandler $cpmHandler,
SurveysNetrworkEpcStatsHandler $epcHandler,
SurveyStatsRepositoryInterface $statsRepository
){
$this->cpmHandler = $cpmHandler;
$this->epcHandler = $epcHandler;
$this->statsRepo = $statsRepository;
}
public function handle() {
$this->cpmHandler->handle();
$this->epcHandler->handle();
}
}
ПредложенияNetrworkCpmStatsHandler:
etrworkCpmStatsHandler:
/** This handle responsible of collectiong, calculating and storing network wide offers CPMs. We can calculate CPM inside DB, so here do not need any Calculator */
class OffersNetrworkCpmStatsHandler implements StatsHandlerInterface {
private $surveyImpressionsRepo;
private $statsRepo;
public function __construct(
SurveyImpressionRepositoryInterface $surveyImpressionRepository,
SurveyStatsRepositoryInterface $statsRepository
){
$this->surveyImpressionsRepo = $surveyImpressionRepository;
$this->statsRepo = $statsRepository;
}
public function handle() {
$stats = [];
$stats = $this->fetchStatisticData();
$this->store($stats)
}
private function fetchStatisticData(){
return $this->surveyImpressionsRepo->getCpm();
}
private function store($stst): bool{
$this->statsRepo->insert()
}
}
CpmCalculator:
1020 ** Я удаляю всю логику проверки здесь и интерфейсы, чтобы уменьшить количество кода.
Может кто-нибудь предложить какие-либо улучшения, может быть, я могу использовать некоторые шаблоны здесь?Спасибо за любые предложения.