-
Notifications
You must be signed in to change notification settings - Fork 9
Refactor codebase with DDD CQRS clean architecture #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Providers; | ||
|
|
||
| use Illuminate\Support\ServiceProvider; | ||
| use Squidmin\Application\Command\AllowedIp\AddAllowedIpCommandHandler; | ||
| use Squidmin\Application\Command\AllowedIp\RemoveAllowedIpCommandHandler; | ||
| use Squidmin\Application\Command\SquidUser\CreateSquidUserCommandHandler; | ||
| use Squidmin\Application\Command\SquidUser\DeleteSquidUserCommandHandler; | ||
| use Squidmin\Application\Command\SquidUser\DisableSquidUserCommandHandler; | ||
| use Squidmin\Application\Command\SquidUser\EnableSquidUserCommandHandler; | ||
| use Squidmin\Application\Command\SquidUser\ModifySquidUserCommandHandler; | ||
| use Squidmin\Application\Command\User\CreateUserCommandHandler; | ||
| use Squidmin\Application\Command\User\DeleteUserCommandHandler; | ||
| use Squidmin\Application\Command\User\ModifyUserCommandHandler; | ||
| use Squidmin\Application\Query\AllowedIp\GetAllowedIpQueryHandler; | ||
| use Squidmin\Application\Query\AllowedIp\GetAllowedIpsByOwnerQueryHandler; | ||
| use Squidmin\Application\Query\AllowedIp\SearchAllowedIpsQueryHandler; | ||
| use Squidmin\Application\Query\SquidUser\GetSquidUserQueryHandler; | ||
| use Squidmin\Application\Query\SquidUser\GetSquidUsersByOwnerQueryHandler; | ||
| use Squidmin\Application\Query\SquidUser\SearchSquidUsersQueryHandler; | ||
| use Squidmin\Application\Query\User\GetAllUsersQueryHandler; | ||
| use Squidmin\Application\Query\User\GetUserQueryHandler; | ||
| use Squidmin\Application\Query\User\SearchUsersQueryHandler; | ||
| use Squidmin\Domain\AllowedIp\AllowedIpRepositoryInterface; | ||
| use Squidmin\Domain\SquidUser\SquidUserRepositoryInterface; | ||
| use Squidmin\Domain\User\UserRepositoryInterface; | ||
| use Squidmin\Infrastructure\Persistence\Eloquent\EloquentAllowedIpRepository; | ||
| use Squidmin\Infrastructure\Persistence\Eloquent\EloquentSquidUserRepository; | ||
| use Squidmin\Infrastructure\Persistence\Eloquent\EloquentUserRepository; | ||
|
|
||
| final class DomainServiceProvider extends ServiceProvider | ||
| { | ||
| public function register(): void | ||
| { | ||
| // Register Repositories | ||
| $this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class); | ||
| $this->app->bind(SquidUserRepositoryInterface::class, EloquentSquidUserRepository::class); | ||
| $this->app->bind(AllowedIpRepositoryInterface::class, EloquentAllowedIpRepository::class); | ||
|
|
||
| // Register User Command Handlers | ||
| $this->app->bind(CreateUserCommandHandler::class); | ||
| $this->app->bind(ModifyUserCommandHandler::class); | ||
| $this->app->bind(DeleteUserCommandHandler::class); | ||
|
|
||
| // Register User Query Handlers | ||
| $this->app->bind(GetUserQueryHandler::class); | ||
| $this->app->bind(GetAllUsersQueryHandler::class); | ||
| $this->app->bind(SearchUsersQueryHandler::class); | ||
|
|
||
| // Register SquidUser Command Handlers | ||
| $this->app->bind(CreateSquidUserCommandHandler::class); | ||
| $this->app->bind(ModifySquidUserCommandHandler::class); | ||
| $this->app->bind(DeleteSquidUserCommandHandler::class); | ||
| $this->app->bind(EnableSquidUserCommandHandler::class); | ||
| $this->app->bind(DisableSquidUserCommandHandler::class); | ||
|
|
||
| // Register SquidUser Query Handlers | ||
| $this->app->bind(GetSquidUserQueryHandler::class); | ||
| $this->app->bind(GetSquidUsersByOwnerQueryHandler::class); | ||
| $this->app->bind(SearchSquidUsersQueryHandler::class); | ||
|
|
||
| // Register AllowedIp Command Handlers | ||
| $this->app->bind(AddAllowedIpCommandHandler::class); | ||
| $this->app->bind(RemoveAllowedIpCommandHandler::class); | ||
|
|
||
| // Register AllowedIp Query Handlers | ||
| $this->app->bind(GetAllowedIpQueryHandler::class); | ||
| $this->app->bind(GetAllowedIpsByOwnerQueryHandler::class); | ||
| $this->app->bind(SearchAllowedIpsQueryHandler::class); | ||
| } | ||
|
|
||
| public function boot(): void | ||
| { | ||
| // | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\AllowedIp; | ||
|
|
||
| use Squidmin\Application\Command\CommandInterface; | ||
|
|
||
| final class AddAllowedIpCommand implements CommandInterface | ||
| { | ||
| public function __construct( | ||
| private readonly string $ipAddress, | ||
| private readonly int $ownerId | ||
| ) { | ||
| } | ||
|
|
||
| public function getIpAddress(): string | ||
| { | ||
| return $this->ipAddress; | ||
| } | ||
|
|
||
| public function getOwnerId(): int | ||
| { | ||
| return $this->ownerId; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\AllowedIp; | ||
|
|
||
| use Squidmin\Application\Command\CommandHandlerInterface; | ||
| use Squidmin\Application\Command\CommandInterface; | ||
| use Squidmin\Domain\AllowedIp\AllowedIp; | ||
| use Squidmin\Domain\AllowedIp\AllowedIpRepositoryInterface; | ||
| use Squidmin\Domain\AllowedIp\ValueObject\IpAddress; | ||
| use Squidmin\Domain\User\ValueObject\UserId; | ||
|
|
||
| final class AddAllowedIpCommandHandler implements CommandHandlerInterface | ||
| { | ||
| public function __construct( | ||
| private readonly AllowedIpRepositoryInterface $allowedIpRepository | ||
| ) { | ||
| } | ||
|
|
||
| public function handle(CommandInterface $command): void | ||
| { | ||
| if (!$command instanceof AddAllowedIpCommand) { | ||
| throw new \InvalidArgumentException('Invalid command type'); | ||
| } | ||
|
|
||
| $ipAddress = new IpAddress($command->getIpAddress()); | ||
| $ownerId = new UserId($command->getOwnerId()); | ||
|
|
||
| if ($this->allowedIpRepository->existsByIpAndOwnerId($ipAddress, $ownerId)) { | ||
| throw new \DomainException('This IP address is already allowed for this user'); | ||
| } | ||
|
|
||
| $allowedIp = AllowedIp::add( | ||
| $this->allowedIpRepository->nextIdentity(), | ||
| $ipAddress, | ||
| $ownerId | ||
| ); | ||
|
|
||
| $this->allowedIpRepository->save($allowedIp); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\AllowedIp; | ||
|
|
||
| use Squidmin\Application\Command\CommandInterface; | ||
|
|
||
| final class RemoveAllowedIpCommand implements CommandInterface | ||
| { | ||
| public function __construct( | ||
| private readonly int $allowedIpId | ||
| ) { | ||
| } | ||
|
|
||
| public function getAllowedIpId(): int | ||
| { | ||
| return $this->allowedIpId; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\AllowedIp; | ||
|
|
||
| use Squidmin\Application\Command\CommandHandlerInterface; | ||
| use Squidmin\Application\Command\CommandInterface; | ||
| use Squidmin\Domain\AllowedIp\AllowedIpRepositoryInterface; | ||
|
|
||
| final class RemoveAllowedIpCommandHandler implements CommandHandlerInterface | ||
| { | ||
| public function __construct( | ||
| private readonly AllowedIpRepositoryInterface $allowedIpRepository | ||
| ) { | ||
| } | ||
|
|
||
| public function handle(CommandInterface $command): void | ||
| { | ||
| if (!$command instanceof RemoveAllowedIpCommand) { | ||
| throw new \InvalidArgumentException('Invalid command type'); | ||
| } | ||
|
|
||
| $allowedIp = $this->allowedIpRepository->findById($command->getAllowedIpId()); | ||
| if ($allowedIp === null) { | ||
| throw new \DomainException('Allowed IP not found'); | ||
| } | ||
|
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🧩 修正案(専用例外を使用) use Squidmin\Application\Command\CommandHandlerInterface;
use Squidmin\Application\Command\CommandInterface;
use Squidmin\Domain\AllowedIp\AllowedIpRepositoryInterface;
+use Squidmin\Domain\AllowedIp\Exception\AllowedIpNotFound;
@@
- throw new \DomainException('Allowed IP not found');
+ throw new AllowedIpNotFound($command->getAllowedIpId());※ 🤖 Prompt for AI Agents |
||
|
|
||
| $allowedIp->remove(); | ||
| $this->allowedIpRepository->delete($allowedIp); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command; | ||
|
|
||
| interface CommandHandlerInterface | ||
| { | ||
| public function handle(CommandInterface $command): void; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command; | ||
|
|
||
| interface CommandInterface | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\SquidUser; | ||
|
|
||
| use Squidmin\Application\Command\CommandInterface; | ||
|
|
||
| final class CreateSquidUserCommand implements CommandInterface | ||
| { | ||
| public function __construct( | ||
| private readonly string $username, | ||
| private readonly string $password, | ||
| private readonly int $ownerId, | ||
| private readonly ?string $fullname = null, | ||
| private readonly ?string $comment = null | ||
| ) { | ||
| } | ||
|
|
||
| public function getUsername(): string | ||
| { | ||
| return $this->username; | ||
| } | ||
|
|
||
| public function getPassword(): string | ||
| { | ||
| return $this->password; | ||
| } | ||
|
|
||
| public function getOwnerId(): int | ||
| { | ||
| return $this->ownerId; | ||
| } | ||
|
|
||
| public function getFullname(): ?string | ||
| { | ||
| return $this->fullname; | ||
| } | ||
|
|
||
| public function getComment(): ?string | ||
| { | ||
| return $this->comment; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\SquidUser; | ||
|
|
||
| use Squidmin\Application\Command\CommandHandlerInterface; | ||
| use Squidmin\Application\Command\CommandInterface; | ||
| use Squidmin\Domain\SquidUser\SquidUser; | ||
| use Squidmin\Domain\SquidUser\SquidUserRepositoryInterface; | ||
| use Squidmin\Domain\SquidUser\ValueObject\Comment; | ||
| use Squidmin\Domain\SquidUser\ValueObject\Fullname; | ||
| use Squidmin\Domain\SquidUser\ValueObject\SquidPassword; | ||
| use Squidmin\Domain\SquidUser\ValueObject\SquidUsername; | ||
| use Squidmin\Domain\User\ValueObject\UserId; | ||
|
|
||
| final class CreateSquidUserCommandHandler implements CommandHandlerInterface | ||
| { | ||
| public function __construct( | ||
| private readonly SquidUserRepositoryInterface $squidUserRepository | ||
| ) { | ||
| } | ||
|
|
||
| public function handle(CommandInterface $command): void | ||
| { | ||
| if (!$command instanceof CreateSquidUserCommand) { | ||
| throw new \InvalidArgumentException('Invalid command type'); | ||
| } | ||
|
|
||
| $username = new SquidUsername($command->getUsername()); | ||
|
|
||
| if ($this->squidUserRepository->existsByUsername($username)) { | ||
| throw new \DomainException('Squid user with this username already exists'); | ||
| } | ||
|
|
||
| $squidUser = SquidUser::create( | ||
| $this->squidUserRepository->nextIdentity(), | ||
| $username, | ||
| new SquidPassword($command->getPassword()), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: fd 'SquidPassword.php' -a -x cat -n {}Repository: 39ff/squid-db-auth-web Length of output: 1033 🏁 Script executed: cat -n src/Application/Command/SquidUser/CreateSquidUserCommandHandler.phpRepository: 39ff/squid-db-auth-web Length of output: 1993 🏁 Script executed: fd 'SquidUser.php' src/Domain -x cat -n {}Repository: 39ff/squid-db-auth-web Length of output: 6160 🏁 Script executed: rg 'hash|Hash|crypt|password' --type php -i -A 3 -B 1Repository: 39ff/squid-db-auth-web Length of output: 50378
コードベースには public static function fromPlainPassword(string $plainPassword): self
{
return new self(password_hash($plainPassword, PASSWORD_DEFAULT));
}
public function verify(string $plainPassword): bool
{
return password_verify($plainPassword, $this->value);
}🤖 Prompt for AI Agents |
||
| new UserId($command->getOwnerId()), | ||
| new Fullname($command->getFullname()), | ||
| new Comment($command->getComment()) | ||
| ); | ||
|
|
||
| $this->squidUserRepository->save($squidUser); | ||
|
Comment on lines
+30
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
同時作成で重複をすり抜ける可能性があるため、永続層の一意制約+保存時の例外ハンドリングで原子性を担保してください。 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\SquidUser; | ||
|
|
||
| use Squidmin\Application\Command\CommandInterface; | ||
|
|
||
| final class DeleteSquidUserCommand implements CommandInterface | ||
| { | ||
| public function __construct( | ||
| private readonly int $squidUserId | ||
| ) { | ||
| } | ||
|
|
||
| public function getSquidUserId(): int | ||
| { | ||
| return $this->squidUserId; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Squidmin\Application\Command\SquidUser; | ||
|
|
||
| use Squidmin\Application\Command\CommandHandlerInterface; | ||
| use Squidmin\Application\Command\CommandInterface; | ||
| use Squidmin\Domain\SquidUser\SquidUserRepositoryInterface; | ||
|
|
||
| final class DeleteSquidUserCommandHandler implements CommandHandlerInterface | ||
| { | ||
| public function __construct( | ||
| private readonly SquidUserRepositoryInterface $squidUserRepository | ||
| ) { | ||
| } | ||
|
|
||
| public function handle(CommandInterface $command): void | ||
| { | ||
| if (!$command instanceof DeleteSquidUserCommand) { | ||
| throw new \InvalidArgumentException('Invalid command type'); | ||
| } | ||
|
|
||
| $squidUser = $this->squidUserRepository->findById($command->getSquidUserId()); | ||
| if ($squidUser === null) { | ||
| throw new \DomainException('Squid user not found'); | ||
| } | ||
|
|
||
| $squidUser->delete(); | ||
| $this->squidUserRepository->delete($squidUser); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ドメイン例外がグローバル
\DomainExceptionになっているLine 31 の
\DomainExceptionは SPL 例外で、Squidmin\Domain\Shared\DomainException系で統一したい場合に捕捉されません。専用のドメイン例外(例:AllowedIpAlreadyExists)を定義して投げる方が一貫します。🧩 修正案(専用例外を使用)
※
AllowedIpAlreadyExistsはSquidmin\Domain\Shared\DomainExceptionを継承するクラスとして新規追加してください。🤖 Prompt for AI Agents