### C# Interface Naming Convention (I*) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This example illustrates the common C# convention of prefixing interface names with 'I', such as `IList` for the `List` class. This practice helps distinguish interfaces from their primary implementations. ```csharp // Example: Interface IList and class List ``` -------------------------------- ### PGN Notation Example Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md PGN (Portable Game Notation) is a standard plain text format for recording chess games. It includes metadata about the game (like event, site, date, players) and the sequence of moves made during the game. ```text [Event "F/S Return Match"] [Site "Belgrade, Serbia JUG"] [Date "1992.11.04"] [Round "29"] [White "Fischer, Robert J."] [Black "Spassky, Boris V."] [Result "1/2-1/2"] 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 ...(все остальные ходы)... 42. g4 Bd3 43. Re6 1/2-1/2 ``` -------------------------------- ### Laravel Interface Definitions and Implementations Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This example illustrates the common Laravel practice of defining interfaces within the `IlluminateContracts` namespace and their corresponding implementations in other namespaces. It highlights potential naming conflicts and the use of aliases for clarity. ```php namespace Illuminate\Contracts\Cache; interface Repository { //... } namespace Illuminate\Contracts\Config; interface Repository { //... } namespace Illuminate\Cache; use Illuminate\Contracts\Cache\Repository as CacheContract; class Repository implements CacheContract { //... } namespace Illuminate\Config; use ArrayAccess; use Illuminate\Contracts\Config\Repository as ConfigContract; class Repository implements ArrayAccess, ConfigContract { //... } ``` -------------------------------- ### PHP Eloquent Read Model Implementations and Controllers Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/10-cqrs.md This PHP code demonstrates concrete Eloquent read model implementations (Client, Freelancer, Proposal, Job) extending the base ReadModel. It also includes controller examples (ClientsController, FreelancersController, JobsController) that fetch data using these read models, including a Job with its proposals. ```php final class Client extends ReadModel{} final class Freelancer extends ReadModel{} final class Proposal extends ReadModel{} final class Job extends ReadModel { public function proposals() { return $this->hasMany(Proposal::class, 'job_id', 'id'); } } final class ClientsController extends Controller { public function get(UuidInterface $id) { return Client::findOrFail($id); } } final class FreelancersController extends Controller { public function get(UuidInterface $id) { return Freelancer::findOrFail($id); } } final class JobsController extends Controller { public function get(UuidInterface $id) { return Job::findOrFail($id); } public function getWithProposals(UuidInterface $id) { return Job::with('proposals')->findOrFail($id); } } ``` -------------------------------- ### PHP: Interfaces and ImageUploader with Mocking for Testing Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Illustrates defining interfaces for dependencies (Storage, ThumbCreator) and creating an ImageUploader class that uses these interfaces. It also includes a basic test case using Mockery for mocking dependencies. ```PHP interface Storage { // методы... } interface ThumbCreator { // методы... } ``` ```PHP final class ImageUploader { /** @var Storage */ private $storage; /** @var ThumbCreator */ private $thumbCreator; public function __construct( Storage $storage, ThumbCreator $thumbCreator) { $this->storage = $storage; $this->thumbCreator = $thumbCreator; } public function upload(...) { $this->thumbCreator->... $this->storage->... } } ``` ```PHP class ImageUploaderTest extends TestCase { public function testSomething() { $storageMock = \Mockery::mock(Storage::class); $thumbCreatorMock = \Mockery::mock(ThumbCreator::class); $imageUploader = new ImageUploader( $storageMock, $thumbCreatorMock ); $imageUploader->upload(...) } } ``` -------------------------------- ### PHP Класс Todo для Event Sourcing Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример класса Todo, наследующего от AggregateRoot. Демонстрирует метод post для создания новой задачи, который записывает событие TodoWasPosted с помощью recordThat. ```PHP final class Todo extends AggregateRoot { /** @var TodoId */ private $todoId; /** @var UserId */ private $assigneeId; /** @var TodoText */ private $text; /** @var TodoStatus */ private $status; public static function post( TodoText $text, UserId $assigneeId, TodoId $todoId): Todo { $self = new self(); $self->recordThat(TodoWasPosted::byUser( ``` -------------------------------- ### Создание пользователя с использованием массива в UserService (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/4-application-layer.md Этот фрагмент кода демонстрирует метод `create` класса `UserService`, который принимает массив данных запроса для создания нового пользователя. Он включает загрузку аватара, сохранение пользователя и отправку приветственного письма. Зависит от `ImageUploader` и `EmailSender`. ```php final class UserService { public function __construct( private ImageUploader $imageUploader, private EmailSender $emailSender) {} public function create(array $request) { $avatarFileName = ...; $this->imageUploader->upload( $avatarFileName, $request['avatar']); $user = new User(); $user->email = $request['email']; $user->name = $request['name']; $user->avatarUrl = $avatarFileName; $user->subscribed = isset($request['subscribed']); $user->birthDate = new DateTime($request['birthDate']); if (!$user->save()) { return false; } $this->emailSender->send($user->email, 'Hi email'); return true; } } // Controller public function store(Request $request, UserService $userService) { $this->validate($request, [ 'email' => 'required|email', 'name' => 'required', 'avatar' => 'required|image', 'birthDate' => 'required|date', ]); if (!$userService->create($request->all())) { return redirect()->back()->withMessage('...'); } return redirect()->route('users'); } ``` -------------------------------- ### FEN Notation Example Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md FEN (Forsyth-Edwards Notation) is a standard notation for describing a particular board position of a chess game. It includes information about piece placement, whose turn it is, castling rights, en passant target square, and halfmove and fullmove counters. ```text rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2 ``` -------------------------------- ### PHP: Controller Method with Dependency Injection Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Shows how a controller method can receive an ImageUploader instance via dependency injection, simplifying the method's logic and improving testability. ```PHP public function store( Request $request, ImageUploader $imageUploader) { //... $avatarFileName = ...; $imageUploader->upload( $avatarFileName, $request->file('avatar') ); //... } ``` -------------------------------- ### Структура таблицы 'post_events' (Event Sourcing) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример структуры таблицы для хранения событий, связанных с постами, в рамках шаблона Event Sourcing. Таблица предназначена только для вставки и чтения событий. ```SQL PostId, EventName, EventDate, EventData ``` -------------------------------- ### PHP Laravel Тесты создания и удаления постов Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/8-unit-test.md Примеры тестов Laravel для создания и удаления постов. Тест 'testCreate' проверяет создание поста через POST запрос и последующее получение данных через GET. Тест 'testDelete' проверяет удаление поста через DELETE запрос и последующую проверку отсутствия. ```php class PostsTest extends TestCase { public function testCreate() { $response = $this->postJson('/api/posts', [ 'title' => 'Post test title' ]); $response ->assertOk() ->assertJsonStructure([ 'id', ]); $checkResponse = $this->getJson( '/api/posts/' . $response->getData()->id); $checkResponse ->assertOk() ->assertJson([ 'title' => 'Post test title', ]); } public function testDelete() { // Здесь некоторая инициализация, чтобы создать // объект Post с id = $postId // Удостоверяемся, что этот объект есть $this->getJson('/api/posts/' . $postId) ->assertOk(); // Удаляем его $this->jsonDelete('/posts/' . $postId) ->assertOk(); // Проверяем, что больше в приложении его нет $this->getJson('/api/posts/' . $postId) ->assertStatus(404); } } ``` -------------------------------- ### Проблема консистентности отношений при передаче сущности в событие (SurveyOptionAdded) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/7-events.md Пример класса-события SurveyOptionAdded, демонстрирующий проблему, когда слушатель получает устаревшую коллекцию отношений ('options') после добавления нового элемента через метод create(). ```php final class SurveyOptionAdded { public function __construct( public readonly Survey $survey ) {} } final class SurveyService { public function addOption(SurveyAddOptionDto $request) { $survey = Survey::findOrFail($request->getSurveyId()); if($survey->options->count() >= Survey::MAX_POSSIBLE_OPTIONS) { throw new BusinessException('Max options amount exceeded'); } $survey->options()->create(...); $this->dispatcher->dispatch(new SurveyOptionAdded($survey)); } } final class SomeListener implements ShouldQueue { public function handle(SurveyOptionAdded $event) { // ... foreach($event->survey->options as $option) { } } } ``` -------------------------------- ### PHP: ImageUploader with Constructor Injection Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Demonstrates the ImageUploader class using constructor injection, where dependencies like Storage and ThumbCreator are passed into the constructor, promoting loose coupling. ```PHP final class ImageUploader { /** @var Storage */ private $storage; /** @var ThumbCreator */ private $thumbCreator; public function __construct( Storage $storage, ThumbCreator $thumbCreator) { $this->storage = $storage; $this->thumbCreator = $thumbCreator; } public function upload(...) { $this->thumbCreator->... $this->storage->... } } ``` -------------------------------- ### PHP Interface and Implementation Naming Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This PHP code demonstrates a recommended approach for naming interfaces and their implementations when multiple implementations exist. The interface uses a natural name, while implementations use descriptive prefixes. ```php interface Storage{} class S3Storage implements Storage{} ``` -------------------------------- ### Обновленный метод create в UserService с UserCreateDto (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/4-application-layer.md Этот фрагмент кода показывает обновленный метод `create` класса `UserService`, который теперь принимает объект `UserCreateDto`. Это упрощает доступ к данным пользователя и повышает читаемость кода. ```php final class UserService { //... public function create(UserCreateDto $request) { $avatarFileName = ...; $this->imageUploader->upload( $avatarFileName, $request->avatarFile); $user = new User(); $user->email = $request->email; $user->avatarUrl = $avatarFileName; $user->subscribed = $request->subscribed; $user->birthDate = $request->birthDate; if (!$user->save()) { return false; } $this->emailSender->send($user->email, 'Hi email'); return true; } } public function store(Request $request, UserService $userService) { ``` -------------------------------- ### UserService с обработкой исключений Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример класса UserService, демонстрирующий метод changePassword, который может выбрасывать ModelNotFoundException и BusinessException. ```php final class UserService { /** * @param ChangeUserPasswordDto $command * @throws ModelNotFoundException * @throws BusinessException */ public function changePassword( ChangeUserPasswordDto $command): void {...} } ``` -------------------------------- ### PHP Абстрактный класс AggregateRoot Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Базовый класс AggregateRoot для сущностей Event Sourcing. Управляет списком записанных событий (recordedEvents), предоставляет методы для получения и сброса событий (popRecordedEvents) и записи новых событий (recordThat). ```PHP abstract class AggregateRoot { /** * List of events that are not committed to the EventStore * * @var AggregateChanged[] */ protected $recordedEvents = []; /** * Get pending events and reset stack * * @return AggregateChanged[] */ protected function popRecordedEvents(): array { $pendingEvents = $this->recordedEvents; $this->recordedEvents = []; return $pendingEvents; } /** * Record an aggregate changed event */ protected function recordThat(AggregateChanged $event): void { $this->version += 1; $this->recordedEvents[] = $event->withVersion($this->version); $this->apply($event); } abstract protected function aggregateId(): string; /** * Apply given event */ abstract protected function apply(AggregateChanged $event); } ``` -------------------------------- ### PHP: Рефакторинг проверки заявок с использованием Proposal::checkCompatibility Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/9-domain-layer.md Этот код представляет собой рефакторинг предыдущего примера. Логика проверки совместимости заявок перенесена в метод Proposal::checkCompatibility, что улучшает инкапсуляцию и уменьшает связанность класса Job. ```PHP final class Proposal { //... /** * @param Proposal $other * @throws BusinessException */ public function checkCompatibility(Proposal $other) { if($this->freelancer->equals($other->freelancer)) { throw new BusinessException( 'Этот фрилансер уже оставлял заявку'); } } } final class Job { /** * ... * @throws BusinessException */ public function addProposal(Freelancer $freelancer, Money $hourRate, string $coverLetter) { $newProposal = new Proposal($this, $freelancer, $hourRate, $coverLetter); foreach($this->proposals as $proposal) { $proposal->checkCompatibility($newProposal); } $this->proposals[] = $newProposal; } } ``` -------------------------------- ### Создание класса UserCreateDto (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/4-application-layer.md Пример создания класса `UserCreateDto` для структурированной передачи данных пользователя. Класс содержит приватные поля и геттер-методы для обеспечения неизменяемости данных. ```php final class UserCreateDto { private string $email; private DateTime $birthDate; private bool $subscribed; public function __construct( string $email, DateTime $birthDate, bool $subscribed) { $this->email = $email; $this->birthDate = $birthDate; $this->subscribed = $subscribed; } public function getEmail(): string { return $this->email; } public function getBirthDate(): DateTime { return $this->birthDate; } public function isSubscribed(): bool { return $this->subscribed; } } ``` -------------------------------- ### Go: Пример открытия файла и обработки ошибок Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример кода на Go, демонстрирующий открытие файла с использованием os.Open и проверку ошибки, возвращаемой функцией. В случае ошибки, она выводится в лог с помощью log.Fatal. ```Go f, err := os.Open("filename.ext") if err != nil { log.Fatal(err) } // do something with the open *File f ``` -------------------------------- ### PHP: Basic Image Upload with Hard Dependency Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This snippet shows a basic implementation of an ImageUploader class with a hard dependency on the ImageUploader class itself, illustrating a less flexible approach. ```PHP $imageUploader = new ImageUploader(); $imageUploader->upload(...); ``` ```PHP ImageUploader::upload(...); ``` -------------------------------- ### PHP: Загрузчик изображений с проверкой контента и блокировкой пользователя Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Основной класс ImageUploader, который управляет процессом загрузки изображений. Он использует ImageGuard для проверки контента, FileSystemManager для сохранения файла и WrongImageUploadsListener для обработки случаев с недопустимым контентом. Метод upload принимает файл, пользователя, папку и опции для управления блокировкой и правилами проверки. ```PHP final class ImageUploader { /** @var ImageGuard */ private $imageGuard; /** @var FileSystemManager */ private $fileSystemManager; /** @var WrongImageUploadsListener */ private $listener; public function __construct( ImageGuard $imageGuard, FileSystemManager $fileSystemManager, WrongImageUploadsListener $listener) { $this->imageGuard = $imageGuard; $this->fileSystemManager = $fileSystemManager; $this->listener = $listener; } /** * @param UploadedFile $file * @param User $uploadedBy * @param string $folder * @param bool $dontBan * @param bool $weakerRules * @return bool|string */ public function upload( UploadedFile $file, User $uploadedBy, string $folder, bool $dontBan = false, bool $weakerRules = false) { $fileContent = $file->getContents(); if (!$this->imageGuard->check($fileContent, $weakerRules)) { if (!$dontBan) { $this->listener->handle($uploadedBy); } return false; } ``` -------------------------------- ### PHP: Создание и использование BusinessException Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Определяет пользовательский класс `BusinessException` для ошибок бизнес-логики, наследуясь от стандартного класса `Exception`. Включает примеры выбрасывания исключения при проверке пароля и его обработки в контроллере с разделением на бизнес-исключения и общие ошибки. ```PHP class BusinessException extends \Exception { /** * @var string */ private $userMessage; public function __construct(string $userMessage) { $this->userMessage = $userMessage; parent::__construct("Business exception"); } public function getUserMessage(): string { return $this->userMessage; } } // Теперь ошибка верификации старого пароля вызовет исключение if(!password_verify($command->getOldPassword(), $user->password)) { throw new BusinessException("Old password is not valid"); } final class UserController { public function changePassword(UserService $service, ChangeUserPasswordRequest $request) { try { $service->changePassword($request->getDto()); } catch(BusinessException $e) { // вернуть ошибочный ответ // с одним из 400-ых кодов // с $e->getUserMessage(); } catch(\Throwable $e) { // залогировать ошибку // вернуть ошибочный ответ (с кодом 500) // с текстом "Houston, we have a problem" // Не возвращая реальный текст ошибки } // вернуть успешный ответ } } ``` -------------------------------- ### PHP: Обработка исключений SameFreelancerProposalException Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/9-domain-layer.md Пример реализации исключения SameFreelancerProposalException, наследующего от BusinessException, и его использование в классах Proposal и Job для проверки совместимости заявок. ```php abstract class BusinessException extends \Exception {...} final class SameFreelancerProposalException extends BusinessException { public function __construct() { parent::__construct( 'Этот фрилансер уже оставлял заявку'); } } final class Proposal { //... /** * @param Proposal $other * @throws SameFreelancerProposalException */ public function checkCompatibility(Proposal $other) { if($this->freelancer->equals($other->freelancer)) { throw new SameFreelancerProposalException(); } } } final class Job { //... /** * @param Proposal $newProposal * @throws SameFreelancerProposalException */ public function addProposal(Proposal $newProposal) { foreach($this->proposals as $proposal) { $proposal->checkCompatibility($newProposal); } $this->proposals[] = $newProposal; } } ``` -------------------------------- ### BusinessException, наследуемый от RuntimeException Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример определения класса BusinessException, наследуемого от RuntimeException, для использования непроверяемых исключений. ```php class BusinessException extends \RuntimeException { ...} ``` -------------------------------- ### Тестирование заявки фрилансера на вакансию (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример unit-теста для проверки логики заявки фрилансера на вакансию. Тест проверяет, что один и тот же фрилансер не может подать заявку на одну и ту же вакансию дважды, используя исключение SameFreelancerProposalException. ```php class JobApplyTest extends UnitTestCase { public function testApplySameFreelancer() { $job = $this->createJob(); $freelancer = $this->createFreelancer(); $freelancer->apply($job, 'cover letter'); $this->expectException(SameFreelancerProposalException::class); $freelancer->apply($job, 'another cover letter'); } } ``` -------------------------------- ### Структура таблицы 'posts' (традиционная) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример традиционной структуры таблицы для хранения данных о постах, включающей ID, заголовок, текст, статус публикации и автора. ```SQL PostId, Title, Text, Published, CreatedBy ``` -------------------------------- ### Dependency Injection with Conflicting Interface Names Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This PHP code demonstrates a class that requires both caching and configuration repositories. It shows how to inject these dependencies using fully qualified namespace names when interface names are ambiguous, such as 'Repository' for both cache and config. ```php use Illuminate\Contracts\Cache\Repository; class SomeClassWhoWantsConfigAndCache { /** @var Repository */ private $cache; /** @var \Illuminate\Contracts\Config\Repository */ private $config; public function __construct(Repository $cache, \Illuminate\Contracts\Config\Repository $config) { $this->cache = $cache; $this->config = $config; } } ``` -------------------------------- ### Примеры событий для поста Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Перечисление возможных событий, которые могут происходить с постом в системе, использующей Event Sourcing. ```None * **PostCreated**(Title, Text, CreatedBy) * **PostPublished**(PublishedBy) * **PostDeleted** ``` -------------------------------- ### Kotlin: Пример final и open классов Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Пример использования ключевых слов 'open' и 'abstract' в Kotlin для управления наследованием. Демонстрирует подход Kotlin к наследованию по умолчанию. ```kotlin open class Foo {} // Или abstract class Bar {} // По умолчанию классы final ``` -------------------------------- ### Configure Laravel Dependency Injection Container Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md This snippet shows how to bind interfaces to their concrete implementations within the Laravel service container. It demonstrates injecting S3Storage for the Storage interface and ImagickThumbCreator for the ThumbCreator interface. ```php $this->app->bind(Storage::class, S3Storage::class); $this->app->bind(ThumbCreator::class, ImagickThumbCreator::class); ``` -------------------------------- ### Реализация LaravelMultiDispatcher (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Класс `LaravelMultiDispatcher`, реализующий интерфейс `MultiDispatcher`. Он использует стандартный `Dispatcher` Laravel для отправки событий и регистрируется в сервис-контейнере. ```PHP use Illuminate\Contracts\Events\Dispatcher; final class LaravelMultiDispatcher implements MultiDispatcher { /** @var Dispatcher */ private $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } public function multiDispatch(array $events) { foreach($events as $event) { $this->dispatcher->dispatch($event); } } } class AppServiceProvider extends ServiceProvider { public function boot() { $this->app->bind( MultiDispatcher::class, LaravelMultiDispatcher::class); } } ``` -------------------------------- ### Тестирование публикации статьи (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример unit-теста для проверки успешной публикации статьи. Тест проверяет, что при вызове метода publish() генерируется событие PostPublished, даже если внутреннее состояние (например, флаг published) не обновляется. ```php class Post { public function publish() { if (empty($this->body)) { throw new CantPublishException(); } //$this->published = true; $this->record(new PostPublished($this->id)); } } class PublishPostTest extends \PHPUnit\Framework\TestCase { public function testSuccessfulPublish() { // initialize $post = new Post('title', 'body'); // run $post->publish(); // check $this->assertEventsHas( PostPublished::class, $post->releaseEvents()); } } ``` -------------------------------- ### Java: Пример выбрасывания исключения Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Демонстрирует базовый пример выбрасывания исключения в Java. Этот код не скомпилируется без соответствующей обработки исключения. ```java public class Foo { public void bar() { throw new Exception("test"); } } ``` -------------------------------- ### Сервисный класс для управления пользователями в PHP Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/4-application-layer.md Пример структуры сервисного класса для инкапсуляции бизнес-логики, связанной с сущностью пользователя. Предоставляет методы для получения, создания и управления пользователями. ```php final class UserService { public function getById(...): User; public function getByEmail(...): User; public function create(...); public function ban(...); ... } ``` -------------------------------- ### PHP CRUD приложение: Метод store с загрузкой аватара и отправкой email Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Расширенный пример метода store в PHP, включающий валидацию поля аватара, загрузку файла в S3, сохранение URL аватара в модели пользователя и отправку email. ```php public function store(Request $request) { $this->validate($request, [ 'email' => 'required|email', 'name' => 'required', 'avatar' => 'required|image', ]); $avatarFileName = ...; \Storage::disk('s3')->put( $avatarFileName, $request->file('avatar')); $user = new User($request->except('avatar')); $user->avatarUrl = $avatarFileName; if (!$user->save()) { return redirect()->back()->withMessage('...'); } \Email::send($user->email, 'Hi email'); return redirect()->route('users'); } ``` -------------------------------- ### Статические методы для генерации ключей кэширования Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Пример использования статических методов в классе CacheKeys для генерации ключей кэширования, что является допустимой практикой для внутренних компонентов. ```PHP final class CacheKeys { public static function getUserByIdKey(int $id) { return sprintf('user_%d_%d', $id, User::VERSION); } public static function getUserByEmailKey(string $email) { return sprintf('user_email_%s_%d', $email, User::VERSION); } //... } $key = CacheKeys::getUserByIdKey($id); ``` -------------------------------- ### Java: Метод, выбрасывающий IOException Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример метода в Java, который может выбросить проверяемое исключение IOException. Сигнатура метода явно указывает на эту возможность. ```java public class File { public String getCanonicalPath() throws IOException { //... } } ``` -------------------------------- ### Java: Вызывающий код для метода с объявленным исключением Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Примеры кода в Java, который вызывает метод, объявленный с выбрасыванием исключения. Демонстрирует оба подхода: объявление исключения или его перехват. ```java public class FooCaller { public void caller() throws Exception { (new Foo)->bar(); } public void caller2() { try { (new Foo)->bar(); } catch(Exception e) { // do something } } } ``` -------------------------------- ### Определение исключений ModelNotFoundException и BusinessException Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Определение пользовательских классов исключений ModelNotFoundException и BusinessException, наследуемых от базового класса Exception. ```php class ModelNotFoundException extends \Exception { ...} ``` ```php class BusinessException extends \Exception { ...} ``` -------------------------------- ### Java: Объявление исключения в сигнатуре метода Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Иллюстрирует, как объявить проверяемое исключение в сигнатуре метода Java с помощью ключевого слова 'throws'. Это обязывает вызывающий код обрабатывать исключение. ```java public class Foo { public void bar() throws Exception { throw new Exception("test"); } } ``` -------------------------------- ### PHP: Описание исключения в phpDoc-комментарии Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Демонстрирует, как указать выбрасываемое исключение в phpDoc-комментарии метода с помощью тега @throws. Это позволяет PhpStorm распознать обработку исключения. ```php class Foo { /** * @throws Exception */ public function bar() { throw new Exception(); } } ``` -------------------------------- ### Маппинг идентификаторов сущностей с атрибутами Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/9-domain-layer.md Примеры маппинга первичных ключей для сущностей 'Freelancer' и 'Proposal' с использованием атрибутов Doctrine. Демонстрирует использование UUID и автоинкрементных целочисленных идентификаторов. ```php use Doctrine\ORM\Mapping AS ORM; #[ORM\Entity] final class Freelancer { #[ORM\Id] #[ORM\Column(type: "uuid", unique: true)] #[ORM\GeneratedValue(strategy: "NONE")] private UuidInterface $id; } #[ORM\Entity] final class Proposal { #[ORM\Id] #[ORM\Column(type: "integer", unique: true)] #[ORM\GeneratedValue(strategy: "AUTO")] private int $id; } ``` -------------------------------- ### UserController с вызовом UserService Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример класса UserController, вызывающего метод changePassword из UserService и обрабатывающего возможные исключения. ```php final class UserController { /** * @param UserService $service * @param Request $request * @throws ModelNotFoundException * @throws BusinessException */ public function changePassword(UserService $service, ChangeUserPasswordRequest $request) { $service->changePassword($request->getDto()); // возвращаем успешный ответ } } ``` -------------------------------- ### Базовый класс для отправки событий (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Абстрактный базовый класс `BaseService`, который инкапсулирует логику отправки нескольких событий через интерфейс `Dispatcher`. Этот подход позволяет избежать дублирования кода в сервисах. ```PHP use Illuminate\Contracts\Events\Dispatcher; abstract class BaseService { /** @var Dispatcher */ private $dispatcher; public function __construct(Dispatcher $dispatcher) { $this->dispatcher = $dispatcher; } protected function dispatchEvents(array $events) { foreach ($events as $event) { $this->dispatcher->dispatch($event); } } } final class SomeService extends BaseService { public function __construct(..., Dispatcher $dispatcher) { parent::__construct($dispatcher); //... } public function someMethod() { //... $this->dispatchEvents($events); } } ``` -------------------------------- ### Сервис для обработки заявок на проект (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/9-domain-layer.md Сервис FreelancersService получает DTO заявки, извлекает сущности Freelancer и Job из базы данных с помощью objectManager, применяет действие к фрилансеру и сохраняет изменения. ```php final class FreelancersService { public function apply(JobApplyDto $dto) { $freelancer = $this->objectManager ->findOrFail(Freelancer::class, $dto->getFreelancerId()); $job = $this->objectManager ->findOrFail(Job::class, $dto->getJobId()); $freelancer->apply($job, $dto->getCoverLetter()); $this->dispatcher->multiDispatch( $freelancer->releaseEvents()); $this->objectManager->flush(); } } ``` -------------------------------- ### Сервисный класс с разделенными операциями чтения/записи и кешированием (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/10-cqrs.md Реализация сервисного класса, где операции записи отделены от операций чтения. Операции чтения вынесены в интерфейс PostQueries и могут быть кешированы с помощью шаблона Декоратор. ```php final class PostService { public function create(PostCreateDto $dto){} public function publish($postId){} public function delete($postId){} } interface PostQueries { public function getById($id): Post; public function getLatestPosts(): array; public function getAuthorPosts($authorId): array; } final class DatabasePostQueries implements PostQueries{} final class CachedPostQueries implements PostQueries { public function __construct( private PostQueries $baseQueries, private Cache $cache, ) {} public function getById($id): Post { return $this->cache->remember('post_' . $id, function() use($id) { return $this->baseQueries->getById($id); }); } //... ``` -------------------------------- ### Создание класса UserCreateDto с readonly полями (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/4-application-layer.md Пример использования `readonly` полей и классов в PHP для создания более лаконичного `UserCreateDto`. Модификатор `readonly` гарантирует неизменяемость, позволяя использовать публичные поля. ```php readonly final class UserCreateDto { public function __construct( public string $email; public DateTime $birthDate; public bool $subscribed; ) {} } ``` -------------------------------- ### PHP Value Object для идентификатора Todo Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/11-es.md Пример реализации Value Object для идентификатора сущности Todo. Использует UUID для обеспечения уникальности и инкапсуляции. Включает методы для генерации, преобразования в строку и сравнения. ```PHP interface ValueObject { public function sameValueAs(ValueObject $object): bool; } final class TodoId implements ValueObject { private function __construct(private UuidInterface $uuid) {} public static function generate(): TodoId { return new self(Uuid::uuid4()); } public static function fromString(string $todoId): TodoId { return new self(Uuid::fromString($todoId)); } public function toString(): string { return $this->uuid->toString(); } public function sameValueAs(ValueObject $other): bool { return \get_class($this) === \get_class($other) && $this->uuid->equals($other->uuid); } } ``` -------------------------------- ### Обработка исключений в контроллере при изменении пароля (PHP) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/5-error-handling.md Пример класса UserController, который вызывает метод изменения пароля из UserService и обрабатывает возможные исключения с помощью блока try-catch. В блоке catch предполагается логирование ошибки и возврат соответствующего веб-ответа. ```php final class UserController { public function changePassword(UserService $service, ChangeUserPasswordRequest $request) { try { $service->changePassword($request->getDto()); } catch(\Throwable $e) { // log error // return failure web response with $e->getMessage(); } // return success web response } } ``` -------------------------------- ### Расширение Dispatcher методом multiDispatch (Kotlin) Source: https://github.com/adelf/acwa_book_ru/blob/master/manuscript/2-di.md Пример использования метода-расширения в Kotlin для добавления функциональности отправки нескольких событий к интерфейсу Dispatcher. Показывает, как можно элегантно расширить существующий интерфейс. ```Kotlin fun Dispatcher.multiDispatch(events: Collection) { events.forEach { dispatch(it) } } ```