[スプリングコア原理]1.サンプルの作成


{単純な例の作成

  • 業務要求と設計
  • 会員ドメイン設計
  • メンバードメイン開発
  • メンバードメイン
  • の実行とテスト
  • 受注および割引ドメイン設計
  • 受注および割引ドメイン開発
  • オーダーおよび割引ドメイン実行およびテスト
  • ビジネスニーズと設計


    会員.
  • 会員の登録と照会ができます.
  • 会員には普通とVIPの2つの等級があります.
  • 会員データは、独自のDBを構築したり、外部システムと連動したりすることができます.(保留中)
  • 注文および割引ポリシー
  • 会員は商品を注文することができます.
  • 会員等級によって割引政策が適用されます.
  • 割引政策は、すべてのVIPに1000ウォンの固定金額割引を要求した.(後で変更するかもしれません.)
  • 割引政策が変わる可能性が高い.会社の基本的な割引政策はまだ確定していないので、開業前に延期したいという悩み.最悪の場合は割引が適用されない可能性があります.(保留中)
  • メンバードメイン設計


    メンバードメインの要件
  • 会員の登録と照会ができます.
  • 会員には普通とVIPの2つの等級があります.
  • 会員データは、独自のDBを構築したり、外部システムと連動したりすることができます.(保留中)
  • メンバードメインのコラボレーション

    会員等級図

    メンバー・オブジェクトのグラフ

    メンバードメイン開発


    会員等級
    package hello.core.member;
    
    public enum Grade {
        BASIC,
        VIP
    }
    メンバーエンティティ
    package hello.core.member;
    
    public class Member {
    
        private Long id;
        private String name;
        private Grade grade;
    
        public Member(Long id, String name, Grade grade) {
            this.id = id;
            this.name = name;
            this.grade = grade;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Grade getGrade() {
            return grade;
        }
    
        public void setGrade(Grade grade) {
            this.grade = grade;
        }
    }
    
    メンバー・リポジトリ・インタフェース
    package hello.core.member;
    
    public interface MemberRepository {
    
        void save(Member member);
    
        Member findById(Long memberId);
    }
    メモリメンバーストレージインプリメンテーション
    package hello.core.member;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class MemoryMemberRepository implements MemberRepository {
    
        private static Map<Long, Member> store = new HashMap<>();
    
        @Override
        public void save(Member member) {
            store.put(member.getId(), member);
        }
    
        @Override
        public Member findById(Long memberId) {
            return store.get(memberId);
        }
    }

    🔔 注意事項


    HashMapで同期性の問題が発生する可能性があります.この場合、ConcurrentHashMapを使用する必要があります.
    会員サービスインタフェース
    package hello.core.member;
    
    public interface MemberService {
    
        void join(Member member);
    
        Member findMember(Long memberId);
    
    }
    会員サービス実施体
    package hello.core.member;
    
    public class MemberServiceImpl implements MemberService {
    
    	private final MemberRepository memberRepository = new
    MemoryMemberRepository();
    
     	public void join(Member member) {
     		memberRepository.save(member);
     	}
        
     	public Member findMember(Long memberId) {
     		return memberRepository.findById(memberId);
     	}
    }
    
    📌 このコードはインタフェースだけでなく,実装体にも依存する.後ろで包装を作り直すつもりです.

    {メンバードメインの実行とテスト


    メンバードメイン-テストメンバー登録
    package hello.core.member;
    
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.Test;
    import static org.junit.jupiter.api.Assertions.*;
    
    class MemberServiceTest {
    
    	MemberService memberService = new MemberServiceImpl();
        
     	@Test
     	void join() {
     		//given
     		Member member = new Member(1L, "memberA", Grade.VIP);
            
     		//when
     		memberService.join(member);
     		Member findMember = memberService.findMember(1L);
            
     		//then
     		Assertions.assertThat(member).isEqualTo(findMember);
     	}
    }

    ❗メンバドメイン設計における問題点


    依存関係はインタフェースだけでなく、実装にも依存します.
    次の問題と解決策は、再設計されます.

    オーダーと割引ドメインの設計


    注文および割引ポリシー
  • 会員は商品を注文することができます.
  • 会員等級によって割引政策が適用されます.
  • 割引政策は、すべてのVIPに1000ウォンの固定金額割引を要求した.(後で変更するかもしれません.)
  • 割引政策が変わる可能性が高い.会社の基本的な割引政策はまだ確定していないので、開業前に延期したいという悩み.最悪の場合は割引が適用されない可能性があります.(保留中)
  • オーダードメインコラボレーション、ロール、責任

    1.注文生成:クライアントは注文サービスに注文を生成するように要求する.
    2.会員照会:割引には会員等級が必要です.そのため、注文サービスは会員リポジトリで会員を検索します.
    3、適用割引:注文サービスは会員等級の割引の有無によって割引政策に委託する.
    4.受注結果を返します.受注サービスは、割引結果を含む受注結果を返します.
    注文ドメイン全体
    🌱 キャラクタとインプリメンテーションを別々にして、インプリメンテーションオブジェクトを自由に組み立てるように設計します.したがって,会員リポジトリを変更するだけでなく,割引ポリシーを柔軟に変更することができる.
    オーダードメインクラス図

    オーダードメインオブジェクトグラフ1
    🌱
    メモリで会員を検索し、定額割引ポリシー(固定金額)をサポートしても注文サービスを変更する必要はありません.ロール間の協力関係を再利用できます.
    発注ドメインオブジェクト図2
    🌱
    会員がメモリではなく、実際のデータベースで照会し、正確率割引ポリシー(受注金額に応じて%割引)をサポートしている場合は、受注サービスを変更する必要はありません.提携関係をそのまま再利用できます.

    オーダーと割引ドメインの開発


    割引ポリシーインタフェース
    package hello.core.discount;
    
    import hello.core.member.Member;
    
    public interface DiscountPolicy {
    
        /**
         *
         * @return 할인 대상 금액
         */
        int discount(Member member, int price);
    }
    固定割引政策実施体
    import hello.core.member.Grade;
    import hello.core.member.Member;
    
    public class FixDiscountPolicy implements DiscountPolicy {
    
        private int discountFixAmount = 1000; // 1000원 할인
    
        @Override
        public int discount(Member member, int price) {
            if (member.getGrade() == Grade.VIP) { // VIP면 1000원 할인, 아니면 할인 없음
                return discountFixAmount;
            } else {
                return 0;
            }
        }
    }
    受注エンティティ
    package hello.core.order;
    
    public class Order {
    
        private Long memberId;
        private String itemName;
        private int itemPrice;
        private int discountPrice;
    
        public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
            this.memberId = memberId;
            this.itemName = itemName;
            this.itemPrice = itemPrice;
            this.discountPrice = discountPrice;
        }
    
        public int calculatePrice() {
            return itemPrice - discountPrice;
        }
    
        public Long getMemberId() {
            return memberId;
        }
    
        public void setMemberId(Long memberId) {
            this.memberId = memberId;
        }
    
        public String getItemName() {
            return itemName;
        }
    
        public void setItemName(String itemName) {
            this.itemName = itemName;
        }
    
        public int getItemPrice() {
            return itemPrice;
        }
    
        public void setItemPrice(int itemPrice) {
            this.itemPrice = itemPrice;
        }
    
        public int getDiscountPrice() {
            return discountPrice;
        }
    
        public void setDiscountPrice(int discountPrice) {
            this.discountPrice = discountPrice;
        }
    
        @Override
        public String toString() {
            return "Order{" +
                    "memberId=" + memberId +
                    ", itemName='" + itemName + '\'' +
                    ", itemPrice=" + itemPrice +
                    ", discountPrice=" + discountPrice +
                    '}';
    
        }
    }
    オーダーサービスインタフェース
    package hello.core.order;
    
    public interface OrderService {
    
        Order createOrder(Long memberId, String itemName, int itemPrice);
    }
    オーダーサービス実施機関
    package hello.core.order;
    
    import hello.core.discount.DiscountPolicy;
    import hello.core.member.Member;
    import hello.core.member.MemberRepository;
    
    public class OrderServiceImpl implements OrderService {
    
        private final MemberRepository memberRepository;
        private final DiscountPolicy discountPolicy;
    
        public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
            this.memberRepository = memberRepository;
            this.discountPolicy = discountPolicy;
        }
    
        @Override
        public Order createOrder(Long memberId, String itemName, int itemPrice) {
            Member member = memberRepository.findById(memberId);
            int discountPrice = discountPolicy.discount(member, itemPrice);
    
            return new Order(memberId, itemName, itemPrice, discountPrice);
        }
    }
    🌱 受注生成要求を受信すると、会員情報が照会され、割引ポリシーが適用され、受注オブジェクトが生成され、返されます.メモリ会員ライブラリと固定金額割引ポリシーを実施体として生成する.

    オーダーと割引ドメインの実行とテスト


    注文および割引ポリシーのテスト
    package hello.core.order;
    
    import hello.core.member.Grade;
    import hello.core.member.Member;
    import hello.core.member.MemberService;
    import hello.core.member.MemberServiceImpl;
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.Test;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    class OrderServiceTest {
    
    	MemberService memberService = new MemberServiceImpl();
     	OrderService orderService = new OrderServiceImpl();
        
     	@Test
     	void createOrder() {
     		long memberId = 1L;
     		Member member = new Member(memberId, "memberA", Grade.VIP);
     		memberService.join(member);
            
     		Order order = orderService.createOrder(memberId, "itemA", 10000);
     		Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
     	}
    }