---
# System prepended metadata

title: 簡易Java Web教學

---

# 簡易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;以上就是簡易的教學範例，若有任何問題或是教學有誤的地方，歡迎提出，謝謝！