# 簡易Java Web教學 ### 前言 &nbsp;&nbsp;在撰寫Java Web時,新手很容易遇到一個問題,到底我要先寫哪一層? 沒有一個標準答案,但是若心中能有一個順序,一定能提升開發及構思的技能,我會建議從外到內寫會比較有一條龍的感覺,剛好在開發的過程中,會知道我現在還有沒加這個等等要記得補上。 ### 實作 &nbsp;&nbsp;我們以一個新增會員功能來做介紹。 ```text controller └─ CreateMemberController.java service └─input └─CreateMemberInput.java └─ CreateMemberService.java repository └─ MemberRepository.java └─ MemberJpaRepository.java domain └─ Member.java entity └─ MemberEntity.java ``` ### DB建置 &nbsp;&nbsp;準備一個會員的表 ```sql= CREATE TABLE member ( member_id INT PRIMARY KEY, name VARCHAR(30), email VARCHAR(255), password VARCHAR(255) ) ``` ### Enity &nbsp;&nbsp;Entity 僅負責與資料庫對應。 ```java= @Data @Builder @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "member") public class MemberEntity { @Id private String memberId; private String name; private String email; private String password; @PrePersist public void prePersist() { if (this.memberId == null) { this.memberId = UUID.randomUUID().toString(); } } public static MemberEntity fromDomain(Member member) { return MemberEntity.builder() .memberId(member.getMemberId()) .name(member.getName()) .email(member.getEmail()) .password(member.getPassword()) .build(); } public Member toDomain() { return Member.builder() .memberId(this.memberId) .name(this.name) .email(this.email) .password(this.password) .build(); } } ``` ### 撰寫Domain &nbsp;&nbsp;Domain 代表系統中的商業概念,透過轉換方法隔離,避免業務邏輯污染 Entity。 ```java= @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @Slf4j public class Member { /** * 會員 id */ private String memberId; /** * 會員中文姓名 */ private String name; /** * 會員 email */ private String email; /** * 會員密碼 */ private String password; private Member(String name, String email, String password) { this.memberId = randomUUID().toString(); this.name = name; this.email = email; this.password = password; } public static Member create(String name, String email, String password) { return new Member(name, email, password); } } ``` ### 撰寫Controller &nbsp;&nbsp;不寫商業邏輯,只負責接收參數,呼叫Service然後回傳結果 ```java= @RestController @RequestMapping("/api") @RequiredArgsConstructor public class CreateMemberController { private final CreateMemberService createMemberService; @PostMapping(value = "/v1/member/create") public ResponseEntity<BaseResponse> createMember( @RequestBody CreateMemberRequest req ) { createMemberService.execute( CreateMemberInput.builder() .name(req.getName()) .email(req.getEmail()) .password(req.getPassword()) .build() ); return ResponseEntity.ok(BaseResponse.success()); } } ``` ### Input ```java= @Getter @Builder public class CreateMemberInput { /** * 會員中文姓名 */ private String name; /** * 會員 email */ private String email; /** * 會員密碼 */ private String password; public void validate() { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("請輸入名字"); } if (StringUtils.isEmpty(email)) { throw new IllegalArgumentException("請輸入Email"); } if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("請輸入密碼"); } } } ``` ### Service ```java= @Service @RequiredArgsConstructor @Slf4j public class CreateMemberService { private final MemberRepository memberRepository; /** * * 建立會員資料Service */ public void execute(CreateMemberInput input) { // validate input input.validate(); final Member member = Member.create(input.getName(), input.getEmail(), input.getPassword()); memberRepository.save(member); } } ``` ### Repository &nbsp;&nbsp;Repository 隔離資料存取細節,讓 Service 不直接依賴 JPA ```java= @Repository @RequiredArgsConstructor @Slf4j public class MemberRepository { private final MemberJpaRepository memberJpaRepository; public void save(Member member) { memberJpaRepository.save(MemberEntity.fromDomain(member)); } } ``` ### JPA ```java= public interface MemberJpaRepository extends JpaRepository<MemberEntity, String> { } ``` ### 總結 &nbsp;&nbsp;以上就是簡易的教學範例,若有任何問題或是教學有誤的地方,歡迎提出,謝謝!