← スキル一覧に戻る

repository
by dairectiv
⭐ 0🍴 0📅 2026年1月3日
SKILL.md
name: repository description: Guide for implementing repositories following Port & Adapters pattern. Use when creating, modifying, or adding methods to repository interfaces or implementations. allowed-tools: Read, Write, Edit, Glob, Grep
Repository Implementation Guide
This Skill provides patterns for implementing repositories following the Port & Adapters (Hexagonal) architecture.
When to Use
- Creating a new repository for an Aggregate Root
- Adding methods to an existing repository interface
- Implementing repository methods in Doctrine
- Writing integration tests for repository queries
Directory Structure
src/{BoundedContext}/
├── Domain/Repository/
│ └── {Entity}Repository.php # Interface (Port)
└── Infrastructure/Doctrine/ORM/Repository/
└── Doctrine{Entity}Repository.php # Implementation (Adapter)
tests/Integration/{BoundedContext}/Infrastructure/Doctrine/ORM/Repository/
└── Doctrine{Entity}RepositoryTest.php
Method Naming Conventions
PHPStan enforces these naming rules via RepositoryMethodRule:
| Prefix | Return Type | Exception | Purpose |
|---|---|---|---|
get | Non-nullable | @throws EntityNotFoundException | Single entity, fail if not found |
find | Nullable (?Entity) | None | Single entity, null if not found |
count | int | None | Count matching entities |
search | array | None | Multiple entities |
Interface Pattern (Domain Layer)
namespace Dairectiv\{Context}\Domain\Repository;
use Dairectiv\{Context}\Domain\Object\{Entity}\Exception\{Entity}NotFoundException;
interface {Entity}Repository
{
public function save({Entity} $entity): void;
/**
* @throws {Entity}NotFoundException
*/
public function get{Entity}ById({Entity}Id $id): {Entity};
public function find{Entity}ById({Entity}Id $id): ?{Entity};
/**
* @return list<{Entity}>
*/
public function searchByCriteria({Entity}SearchCriteria $criteria, int $offset, int $limit): array;
public function countByCriteria({Entity}SearchCriteria $criteria): int;
}
Implementation Pattern (Infrastructure Layer)
namespace Dairectiv\{Context}\Infrastructure\Doctrine\ORM\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
/**
* @extends ServiceEntityRepository<{Entity}>
*/
final class Doctrine{Entity}Repository extends ServiceEntityRepository implements {Entity}Repository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, {Entity}::class);
}
public function save({Entity} $entity): void
{
$this->getEntityManager()->persist($entity);
}
public function get{Entity}ById({Entity}Id $id): {Entity}
{
$entity = $this->find($id);
if (null === $entity) {
throw {Entity}NotFoundException::fromId($id);
}
return $entity;
}
}
Testing Pattern
#[Group('integration')]
#[Group('{bounded-context}')]
#[Group('doctrine-repository')]
final class Doctrine{Entity}RepositoryTest extends IntegrationTestCase
{
private Doctrine{Entity}Repository $repository;
protected function setUp(): void
{
parent::setUp();
$this->repository = self::getService(Doctrine{Entity}Repository::class);
}
public function testItShouldSaveAndGetById(): void
{
$entity = self::createEntity();
$this->repository->save($entity);
$this->getEntityManager()->flush();
$this->getEntityManager()->clear();
$found = $this->repository->get{Entity}ById($entity->id);
self::assertTrue($entity->id->equals($found->id));
}
public function testItShouldThrowExceptionWhenNotFound(): void
{
$id = {Entity}Id::fromString('non-existent');
$this->expectException({Entity}NotFoundException::class);
$this->repository->get{Entity}ById($id);
}
}
Key Rules
- Interface in Domain, Implementation in Infrastructure - Never put Doctrine code in Domain layer
- One repository per Aggregate Root - Repositories are for aggregate roots only, not child entities
- No flush() in repository - Let the caller control transaction boundaries
- Use QueryBuilder for complex queries - Extract criteria application to private methods
- Always test with real database - Integration tests, not mocks
Reference Files
api/src/Authoring/Domain/Repository/RuleRepository.phpapi/src/Authoring/Infrastructure/Doctrine/ORM/Repository/DoctrineRuleRepository.phpapi/tests/Integration/Authoring/Infrastructure/Doctrine/ORM/Repository/DoctrineRuleRepositoryTest.phpapi/tools/phpstan/src/Rules/RepositoryMethodRule.php
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です