Creating a sample program using the problem of a database specialist in DDD Improvement 1

Introduction

Last time, I wrote an article that I tried to make a sample program with DDD using the problem of database specialist.

I made a sample program using the problem of database specialist in Domain Driven Design

Thank you to everyone who commented in the comments section and on twitter. There were many things that didn't go well, and I think it's important to improve and continue to output, so this article was the one I wanted to post a refactored article.

Of the three endpoints, "Apply for entry slot", "Register lottery results", and "Deposit", "Apply for entry slot" has been refactored this time.

The source code is here The version at the time of posting this article is tag 1.1.

Create a domain model diagram

I felt that modeling wasn't working well, so @ little_hand_s's [What is DDD modeling and how to put it into code](https://www.slideshare.net/koichiromatsuoka/domain-modeling- I created a domain model diagram with reference to the slide called andcoding).

参加申込.png

The part corresponding to the business rule is blown out from the exam questions. All of these balloons are not related to the program of this entry point, but it was nice to be able to organize the logic to be written in the domain layer.

From application layer to domain layer

In the previous program, the part "Members can apply for participation in only one entry frame for one tournament" was leaked to the application layer, so I tried to move it to the domain layer.

To @ a-suenami in the previous comment section

For example, I think that the condition that "only one entry frame can be applied for one tournament" is a concern of the domain, so instead of using if in the application service, create a type like the tournament application acceptance policy (FestivalApplicationPolicy). I think it's better to put it in the domain model.

I received the opinion that I implemented this in my own way.

FestivalApplicationPolicy.java


public class FestivalApplicationPolicy {

  private List<Application> applicationList;

  public FestivalApplicationPolicy(List<Application> applicationList) {
    this.applicationList = new ArrayList<>(applicationList);
  }

  /**
   *Returns whether the member specified by the argument has already applied for participation in the tournament specified by the argument.
   *Returns true if already applied.
   */
  boolean hasAlreadyApplyForSameFestival(MemberId memberId, FestivalId festivalId) {

    for (Application application : applicationList) {
      if (application.festivalId().equals(festivalId)
          && application.memberId().equals(memberId)) {
        return true;
      }
    }

    return false;
  }
}

I'm using a first class collection. I have a list of participation applications in the field, and if there are participation applications with the same membership number and tournament number, I tried to judge that participation application has already been completed.

I tried to use this in the domain service described later.

Creating a domain service

I need to create an entity to persist the application, and I created a domain service to create that entity. (The name is confusing, but I am creating it in the domain layer instead of the application layer) I'm still not sure how to use the domain service well, and I was wondering whether to create it with the static method in the entity of the participation application, but this time I did it like this.

ApplicationService.java


public class ApplicationService {

  private Entry entry;

  private FestivalApplicationPolicy festivalApplicationPolicy;

  /**
   *constructor.
   */
  public ApplicationService(
      Entry entry, FestivalApplicationPolicy festivalApplicationPolicy) {

    this.entry = entry;
    this.festivalApplicationPolicy = festivalApplicationPolicy;
  }

  /**
   *Create and return a participation application object.
   * @return Participation application object
   */
  public Application createApplication(MemberId memberId, LocalDate applicationDate)
      throws EntryStatusIsNotRecruitingException,
      HasAlreadyApplyForSameFestivalException {

    //Participation applications will be accepted only while the entry slot is open
    if (entry.entryStatus() != EntryStatus.recruiting) {
      throw new EntryStatusIsNotRecruitingException();
    }

    //Judgment based on the recruitment start date and recruitment end date should be made if the entry frame status is currently recruiting.
    //Since no error should occur, I dare to throw an IllegalStateException here.
    if (applicationDate.compareTo(entry.applicationStartDate()) < 0) {
      throw new IllegalStateException("The specified tournament has not started recruiting yet");
    }

    if (applicationDate.compareTo(entry.applicationEndDate()) > 0) {
      throw new IllegalStateException("The recruitment period for the designated tournament has passed");
    }


    //Members can apply for participation in only one entry slot for one tournament
    if (festivalApplicationPolicy.hasAlreadyApplyForSameFestival(
        memberId, entry.festivalId())) {
      throw new HasAlreadyApplyForSameFestivalException();
    }

    return Application.createEntityForEntry(
        entry.festivalId(),
        memberId,
        entry.entryId(),
        applicationDate
    );
  }
}

After checking "Accept participation application only while the entry frame is being recruited", "Members can apply for participation only for one entry frame for one tournament", etc., if there is no error, the object of Application class to be persisted Is generated and returned.

We also create business exception classes ʻEntryStatusIsNotRecruitingException and HasAlreadyApplyForSameFestivalException`. This was also given by @ a-suenami

I want to handle the message string itself to be shown to the user in the application service, but I think that what kind of error there is can be a domain object, so I want to return it as an enumeration type.

I tried it with reference to the comment. However, I'm using exceptions instead of enums. I was wondering if the business exception class field had an enumeration type that shows the type of error, but I thought that these two errors were also of interest to the participation application domain, so I created an exception class in the participation application domain package. This exception is caught in the application layer so that error messages can be handled.

Application layer comparison

The programs for application for participation in the previous and current application layers are as follows.

** Last time **

ApplicationCommandService.java


  /**
   *Apply for entry slot.
   */
  public void applyForEntry(ApplyForEntryRequest request) {

    final FestivalId festivalId = request.festivalId();
    final MemberId memberId = request.memberId();
    final EntryId entryId = request.entryId();
    final LocalDate applicationDate = request.getApplicationDate();

    final Member member = memberRepository.findMember(request.memberId());
    if (member == null) {
      throw new BusinessErrorException("It is a member that does not exist");
    }

    final Application alreadyApplication =
        applicationRepository.findApplication(festivalId, memberId);

    if (alreadyApplication != null) {
      throw new BusinessErrorException("I have already applied for the specified tournament");
    }

    final Entry entry = entryRepository.findEntry(festivalId, entryId);

    if (entry == null) {
      throw new BusinessErrorException("It is an entry frame that does not exist");
    }

    final Application application = Application.createEntityForEntry(
        festivalId,
        memberId,
        entryId,
        applicationDate
    );

    entry.validateAndThrowBusinessErrorIfHasErrorForApplication(application);

    entry.incrementApplicationNumbers();
    entryRepository.saveEntry(entry);

    applicationRepository.addApplication(application);
  }

this time

ApplicationCommandService.java


  /**
   *Apply for entry slot.
   */
  public void applyForEntry(ApplyForEntryRequest request) {

    final FestivalId festivalId = request.festivalId();
    final EntryId entryId = request.entryId();
    final MemberId memberId = request.memberId();
    final LocalDate applicationDate = request.getApplicationDate();

    final Member member = memberRepository.findMember(memberId);
    if (member == null) {
      throw new BusinessErrorException("It is a member that does not exist");
    }

    final Entry entry = entryRepository.findEntry(festivalId, entryId);
    if (entry == null) {
      throw new BusinessErrorException("It is an entry frame that does not exist");
    }

    final FestivalApplicationPolicy festivalApplicationPolicy =
        applicationRepository.createFestivalApplicationPolicy(festivalId, memberId);

    final ApplicationService applicationService =
        new ApplicationService(entry, festivalApplicationPolicy);

    final Application application;
    try {
      application = applicationService.createApplication(memberId, applicationDate);
    } catch (EntryStatusIsNotRecruitingException e) {
      throw new BusinessErrorException("The designated tournament is not currently recruiting");
    } catch (HasAlreadyApplyForSameFestivalException e) {
      throw new BusinessErrorException("I have already applied for the specified tournament");
    }

    entry.incrementApplicationNumbers();
    entryRepository.saveEntry(entry);

    applicationRepository.addApplication(application);
  }

Generate the domain service ʻApplicationService with the entry frame object ʻEntry and the newly createdFestivalApplicationPolicy as arguments, and generate the participation application entity ʻApplicaiton with ʻApplicationService and make it persistent. I made it. It looks like there isn't much change, but at least we were able to move "members can apply for only one entry slot for one tournament" to the domain layer.

at the end

In this case, it may not be very beneficial, but by repeating the improvement that brings the domain's interests to the domain layer, the program will be easy to change in actual business! !! It should be: sweat_smile: Also, by repeating improvements with refactoring, design skills should improve, so I will refactor the entry points of "register lottery results" and "deposit" in the near future and write an article.

Thank you for reading this far. Comments from everyone I am very happy and have a lot to learn, so I would appreciate it if you could comment.

Recommended Posts

Creating a sample program using the problem of a database specialist in DDD Improvement 2
Creating a sample program using the problem of a database specialist in DDD Improvement 1
I tried to make a sample program using the problem of database specialist in Domain Driven Design
Sample program that returns the hash value of a file in Java
Using the database (SQL Server 2014) from a Java program 2018/01/04
A program that counts the number of words in a List
A memorandum of the FizzBuzz problem
Order of processing in the program
Creating a Servlet in the Liberty environment
I made a sample of how to write delegate in SwiftUI 2.0 using MapKit
Measure the size of a folder in Java
Sample code to assign a value in a property file to a field of the expected type
Count the number of occurrences of a string in Ruby
I tried using a database connection in Android development
About the problem of deadlock in parallel processing in gem'sprockets' 4.0
I wrote a sequence diagram of the j.u.c.Flow sample
[Ruby] Get in the habit of using the dup method when making a copy of a string variable
A program (Java) that outputs the sum of odd and even numbers in an array
Sample code to get the values of major SQL types in Java + Oracle Database 12c
Create a database of all the books that have been circulating in Japan in the last century
I made a program in Java that solves the traveling salesman problem with a genetic algorithm
Template creation program when using the reminder function in slack
A quick explanation of the five types of static in Java
Determine that the value is a multiple of 〇 in Ruby
Procedure for publishing an application using AWS (4) Creating a database
Explanation of Ruby on rails for beginners ③ ~ Creating a database ~
Find the number of days in a month with Kotlin