0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring AOPによる階層データ論理削除の実践例

Last updated at Posted at 2025-11-18

Webアプリケーションで、プロジェクトや掲示板のような階層構造データ(例:プロジェクト → ボード → 投稿)を削除する際、どうすればコアロジック付随処理をきれいに分離できるでしょうか?

この記事では、Spring AOPを使って「親データ削除時に子データも安全に論理削除する方法」をサンプルとともにまとめます。

💡 要件

  • ひとつのプロジェクト(Project) 配下に複数の**ボード(Board)**が存在
  • プロジェクト削除時、関連するボードも**論理削除(delete_yn = 'Y')**する必要あり
  • コアロジック(プロジェクト削除)と付随ロジック(子データの論理削除)は分離したい
  • サービスの依存関係をシンプルにし、保守性を高める

AOPとは?

  • *AOP(アスペクト指向プログラミング)は、「共通部分のコードは一ヶ所にまとめ、必要な場所で自動的に適用する」ための手法です。ログ記録・トランザクション制御・認可処理、そして今回のような「関連データの一括削除」といった横断的関心事(クロスカッティングコンサーン)**に頻繁に活用されます。

1. 依存関係の追加

// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-aop'

2. AOP実装例

@Aspect
@Component
public class ProjectDeletionAspect {

    private static final Logger logger = LoggerFactory.getLogger(ProjectDeletionAspect.class);

    @Autowired
    private BoardService boardService;

    *// ProjectService.deleteProject実行時のみ適用*
    @Around("execution(* com.example.service.ProjectService.deleteProject(..))")
    public Object handleProjectDeletion(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();

        if (args.length == 0 || !(args[0] instanceof Long)) {
            throw new IllegalArgumentException("削除対象のプロジェクトIDが必要です。");
        }

        Long projectId = (Long) args[0];
        logger.info("プロジェクト{}の削除を準備中", projectId);

        *// 1. 元のプロジェクト削除ロジックを実行*
        Object result = joinPoint.proceed();

        *// 2. 配下のボードを論理削除*
        List<Board> boards = boardService.getBoardsByProjectId(projectId);
        for (Board board : boards) {
            boardService.markBoardAsDeleted(board.getId());
            logger.info("ボード{}を論理削除しました。", board.getId());
        }

        logger.info("プロジェクト{}の削除処理が完了しました", projectId);

        return result;
    }


}
  • ポイントカット: **ProjectService.deleteProject**実行時
  • アドバイス: プロジェクト削除後、配下のボードを論理削除

3. テストコード例

@SpringBootTest
class ProjectDeletionAspectTest {

    @Autowired
    private ProjectService projectService;

    @Autowired
    private BoardService boardService;

    @Test
    void projectDeletionAlsoDeletesBoards() {
        Long projectId = 1L; //削除するプロジェクトの番号

        projectService.deleteProject(projectId);

        List<Board> boards = boardService.getBoardsByProjectId(projectId);
        assertTrue(boards.stream().allMatch(b -> b.getDeleteYn().equals("Y")));
    }
}
  • テストポイント:

    プロジェクト削除後、関連ボードすべてが論理削除されているかを検証

4. 要点まとめ・実務Tips

  • コアビジネスロジック(プロジェクト削除)と付随処理(子データ削除)の分離が明確
    • サービスごとの「役割分担」が守れる
  • AOPで「複数箇所に発生しうる子データ処理」を一元管理
  • このやり方でサービス間の強結合を防止し、保守性・拡張性が向上
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?