成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

DDD 很難,推薦一套小白也能輕松落地的方案!

開發
本文我們按照 DDD 的分層原則詳細地落地了一個user添加和查詢功能,并實現支持多種持久化機制(MySQL 和 DynamoDB)的倉儲層設計。

DDD是微服務中經常用到的一種架構方式,在實際工作中,我們該如何快速落地一個 DDD工程呢?這篇文章,我們將手把手帶你落地一個 DDD項目,不管你有沒有 DDD經驗,都可以輕松使用。

在開始我們的文章之前,我們還是要簡單的了解下 DDD是什么,幫助我們下面更好地理解代碼工程。

一、什么是DDD?

DDD,全稱 Domain-Driven Design,翻譯為領域驅動設計,它是一種軟件開發方法論,由埃里克·埃文斯(Eric Evans) 在其2003年出版的同名書籍中提出。DDD旨在通過密切關注復雜軟件系統的核心業務領域,將業務需求與技術實現緊密結合,從而提高軟件的可維護性、可擴展性和靈活性。

1. DDD 的核心理念

DDD 以領域為核心,強調將業務領域作為軟件開發的核心,致力于深入理解業務需求和業務規則,通過建模來反映實際業務問題。

2. 統一語言

DDD使得開發團隊和業務專家共同使用的一種準確、一致的語言(Ubiquitous Language),用于描述業務領域中的概念、流程和規則,減少溝通障礙,提高理解一致性。

3. 戰略設計與戰術設計

DDD 分為戰略設計和戰術設計兩部分:

  • 戰略設計:關注整個系統的高層次結構和模塊劃分,定義不同的子域(Subdomain)和上下文邊界(Bounded Context)。
  • 戰術設計:關注特定上下文內的細節,實現領域模型和相關組件。

說實話,DDD的理論確實很燒腦,我們會在后續的文章中慢慢拆解。不管怎樣,在對 DDD有了簡單的了解之后,我們要進入今天的核心部分:DDD代碼實操。

本文目標:使用DDD + SpringBoot + JPA + 雙數據源(MySQL + DynamoDB)實現對 user表進行添加和查詢功能,完全適合小白操作。

二、項目整體結構

首先,我們先看下整個工程建的主要模塊以及模塊之間的依賴關系:

  • domain:核心領域模型和業務邏輯。
  • repository:倉儲接口定義。
  • application:應用服務層,協調領域對象和倉儲。
  • infrastructure:基礎設施層,包括具體的倉儲實現(MySQL 和 DynamoDB)、配置等。
  • config:獨立的配置模塊(可選,視項目復雜程度而定)。
  • api(或 web):入口層,如 REST API 控制器,主要處理外部接口請求。

模塊結構如下:

ddd-project/
├── build.gradle
├── settings.gradle
├── domain/
│   └── build.gradle
├── repository/
│   └── build.gradle
├── application/
│   └── build.gradle
├── infrastructure/
│   ├── build.gradle
│   ├── persistence-mysql/
│   │   └── build.gradle
│   └── persistence-dynamodb/
│       └── build.gradle
├── config/
│   └── build.gradle
└── api/
    └── build.gradle

三、項目模塊詳解

1. Gradle 配置

(1) 根項目 settings.gradle

在根項目的 settings.gradle 中,包含所有子模塊:

rootProject.name = 'ddd-project'

include 'domain'
include 'repository'
include 'application'
include 'infrastructure'
include 'infrastructure:persistence-mysql'
include 'infrastructure:persistence-dynamodb'
include 'config'
include 'api'

(2) 根項目 build.gradle

根項目的 build.gradle 通常用于定義全局的插件和依賴管理。

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.0' apply false
    id 'io.spring.dependency-management' version '1.1.0' apply false
}

allprojects {
    group = 'com.example'
    version = '1.0.0'
    
    repositories {
        mavenCentral()
    }
}

subprojects {
    apply plugin:'java'
    
    sourceCompatibility = '17'
    targetCompatibility = '17'

    dependencies {
        // 通用依賴,可以在此處添加
        implementation 'org.springframework.boot:spring-boot-starter'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }

    // 統一的測試任務配置等(可選)
}

2. 各子模塊配置

(1) domain 模塊

功能:定義核心領域模型和業務邏輯。

domain/build.gradle:

dependencies {
    // 無需依賴其他模塊,專注于領域邏輯
}

示例代碼:

// domain/src/main/java/com/example/domain/User.java
package com.example.domain;

publicclass User {
    private String id;
    private String name;
    private String email;

    // 構造函數、Getters 和 Setters

    public User() {}

    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // getters and setters
    public String getId() {
        return id;
    }

    // ... 其他 getters 和 setters
}

(2) repository 模塊

功能:定義倉儲接口,供應用層和基礎設施層引用。

repository/build.gradle:

dependencies {
    implementation project(':domain')
}

示例代碼:

// repository/src/main/java/com/example/repository/UserRepository.java
package com.example.repository;

import com.example.domain.User;
import java.util.Optional;
import java.util.List;

public interface UserRepository {
    void save(User user);
    Optional<User> findById(String id);
}

(3) application 模塊

功能:實現應用服務,協調領域模型和倉儲接口。

application/build.gradle:

dependencies {
    implementation project(':domain')
    implementation project(':repository')
    implementation 'org.springframework.boot:spring-boot-starter'
}

示例代碼:

// application/src/main/java/com/example/application/UserService.java
package com.example.application;

import com.example.domain.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
publicclass UserService {

    privatefinal UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository){
        this.userRepository = userRepository;
    }

    public void createUser(User user){
        userRepository.save(user);
    }

    public Optional<User> getUserById(String id){
        return userRepository.findById(id);
    }
}

(4) infrastructure 模塊

功能:實現基礎設施組件,包括具體的倉儲實現。

infrastructure/build.gradle:

dependencies {
    implementation project(':repository')
    implementation 'org.springframework.boot:spring-boot-starter'
}

① infrastructure:persistence-mysql子模塊

功能:實現 MySQL 的具體倉儲。

infrastructure/persistence-mysql/build.gradle:

plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(':domain')
    implementation project(':repository')
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'mysql:mysql-connector-java'
    
    // 可選:MapStruct 用于對象映射
    implementation 'org.mapstruct:mapstruct:1.5.5.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}

示例代碼:


// infrastructure/persistence-mysql/src/main/java/com/example/infrastructure/persistence/mysql/UserEntity.java
package com.example.infrastructure.persistence.mysql;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "users")
publicclass UserEntity {
    @Id
    private String id;
    private String name;
    private String email;

    // 構造函數、Getters 和 Setters

    public UserEntity() {}

    public UserEntity(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // getters and setters
}
// infrastructure/persistence-mysql/src/main/java/com/example/infrastructure/persistence/mysql/ JpaUserRepository.java
package com.example.infrastructure.persistence.mysql;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface JpaUserRepository extends JpaRepository<UserEntity, String> {
}
// infrastructure/persistence-mysql/src/main/java/com/example/infrastructure/persistence/mysql/MySQLUserRepository.java
package com.example.infrastructure.persistence.mysql;

import com.example.domain.User;
import com.example.repository.UserRepository;
import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Optional;
import java.util.List;
import java.util.stream.Collectors;

@Repository
publicclass MySQLUserRepository implements UserRepository {

    privatefinal JpaUserRepository jpaUserRepository;

    @Autowired
    public MySQLUserRepository(JpaUserRepository jpaUserRepository){
        this.jpaUserRepository = jpaUserRepository;
    }

    @Override
    public void save(User user) {
        UserEntity entity = new UserEntity(user.getId(), user.getName(), user.getEmail());
        jpaUserRepository.save(entity);
    }

    @Override
    public Optional<User> findById(String id) {
        Optional<UserEntity> entityOpt = jpaUserRepository.findById(id);
        return entityOpt.map(entity -> new User(entity.getId(), entity.getName(), entity.getEmail()));
    }
}

② infrastructure:persistence-dynamodb 子模塊

功能:實現 DynamoDB 的具體倉儲。

infrastructure/persistence-dynamodb/build.gradle:

plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(':domain')
    implementation project(':repository')
    implementation 'software.amazon.awssdk:dynamodb:2.20.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0'
    implementation 'org.springframework.boot:spring-boot-starter'
}

示例代碼:

// infrastructure/persistence-dynamodb/src/main/java/com/example/infrastructure/persistence/dynamodb/DynamoDBUserRepository.java
package com.example.infrastructure.persistence.dynamodb;

import com.example.domain.User;
import com.example.repository.UserRepository;
import org.springframework.stereotype.Repository;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Optional;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Repository
publicclass DynamoDBUserRepository implements UserRepository {

    privatestaticfinal String TABLE_NAME = "Users";

    privatefinal DynamoDbClient dynamoDbClient;
    privatefinal ObjectMapper objectMapper;

    @Autowired
    public DynamoDBUserRepository(DynamoDbClient dynamoDbClient){
        this.dynamoDbClient = dynamoDbClient;
        this.objectMapper = new ObjectMapper();
    }

    @Override
    public void save(User user) {
        try {
            String json = objectMapper.writeValueAsString(user);
            Map<String, Object> map = objectMapper.readValue(json, Map.class);
            Map<String, AttributeValue> item = map.entrySet().stream()
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            e -> AttributeValue.builder().s(e.getValue().toString()).build()
                    ));

            PutItemRequest request = PutItemRequest.builder()
                    .tableName(TABLE_NAME)
                    .item(item)
                    .build();

            dynamoDbClient.putItem(request);
        } catch (Exception e) {
            thrownew RuntimeException("Failed to save user to DynamoDB", e);
        }
    }

    @Override
    public Optional<User> findById(String id) {
        GetItemRequest request = GetItemRequest.builder()
                .tableName(TABLE_NAME)
                .key(Map.of("id", AttributeValue.builder().s(id).build()))
                .build();

        GetItemResponse response = dynamoDbClient.getItem(request);
        if (response.hasItem()) {
            try {
                String json = objectMapper.writeValueAsString(response.item());
                User user = objectMapper.readValue(json, User.class);
                return Optional.of(user);
            } catch (Exception e) {
                thrownew RuntimeException("Failed to parse user from DynamoDB", e);
            }
        }
        return Optional.empty();
    }
}

③ DynamoDB 客戶端配置

在 infrastructure:persistence-dynamodb 子模塊中配置 DynamoDB 客戶端 Bean。

// infrastructure/persistence-dynamodb/src/main/java/com/example/infrastructure/persistence/dynamodb/DynamoDBConfig.java
package com.example.infrastructure.persistence.dynamodb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.regions.Region;

@Configuration
publicclass DynamoDBConfig {
    @Bean
    public DynamoDbClient dynamoDbClient() {
        return DynamoDbClient.builder()
                .region(Region.US_EAST_1) // 根據實際情況選擇區域
                .build();
    }
}

(5) config 模塊(可選)

功能:集中管理項目配置,例如選擇使用的倉儲實現、數據庫配置等。

config/build.gradle:

dependencies {
    implementation project(':application')
    implementation project(':infrastructure:persistence-mysql')
    implementation project(':infrastructure:persistence-dynamodb')
    implementation 'org.springframework.boot:spring-boot-starter'
}

示例代碼:

// config/src/main/java/com/example/config/RepositoryConfig.java
package com.example.config;

import com.example.repository.UserRepository;
import com.example.infrastructure.persistence_mysql.MySQLUserRepository;
import com.example.infrastructure.persistence_dynamodb.DynamoDBUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
publicclass RepositoryConfig {

    @Value("${app.repository.type}")
    private String repositoryType;

    privatefinal MySQLUserRepository mySQLUserRepository;
    privatefinal DynamoDBUserRepository dynamoDBUserRepository;

    @Autowired
    public RepositoryConfig(MySQLUserRepository mySQLUserRepository, DynamoDBUserRepository dynamoDBUserRepository){
        this.mySQLUserRepository = mySQLUserRepository;
        this.dynamoDBUserRepository = dynamoDBUserRepository;
    }

    @Bean
    public UserRepository userRepository() {
        if ("mysql".equalsIgnoreCase(repositoryType)) {
            return mySQLUserRepository;
        } elseif ("dynamodb".equalsIgnoreCase(repositoryType)) {
            return dynamoDBUserRepository;
        } else {
            thrownew IllegalArgumentException("Unsupported repository type: " + repositoryType);
        }
    }
}

**application.properties**(在 api 模塊或根模塊)

# application.properties
app.repository.type=mysql
# 或者
# app.repository.type=dynamodb

(6) api 模塊

功能:作為應用的入口,處理外部請求(如 REST API)。

api/build.gradle:

plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(':application')
    implementation project(':config')
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

示例代碼:

// api/src/main/java/com/example/api/UserController.java
package com.example.api;

import com.example.application.UserService;
import com.example.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/users")
publicclass UserController {

    privatefinal UserService userService;

    @Autowired
    public UserController(UserService userService){
        this.userService = userService;
    }

    @PostMapping
    public void createUser(@RequestBody User user){
        userService.createUser(user);
    }

    @GetMapping("/{id}")
    public Optional<User> getUserById(@PathVariable String id){
        return userService.getUserById(id);
    }
}

4. 模塊間依賴關系圖

domain
   ↑
repository
   ↑
application
   ↑
infrastructure
   ↑
config
   ↑
api
  • domain 是最底層,其他模塊依賴于它。
  • repository 依賴于 **domain**。
  • application 依賴于 repository 和 **domain**。
  • infrastructure 依賴于 repository 和 **domain**,實現具體倉儲。
  • config 依賴于 application 和 **infrastructure**,進行配置管理。
  • api 依賴于 application 和 **config**,作為應用入口。

四、實施步驟詳解

1. 創建各個模塊

使用 Gradle 命令或 IDE 創建各個模塊。例如,使用命令行:

mkdir ddd-project
cd ddd-project
gradle init --type basic
# 創建子模塊目錄
mkdir domain repository application infrastructure
mkdir infrastructure/persistence-mysql infrastructure/persistence-dynamodb
mkdir config api

然后,在各個子模塊目錄下創建相應的 build.gradle 文件并添加內容,如上所示。

2. 配置依賴關系

確保每個子模塊的 build.gradle 文件中正確地聲明依賴關系。例如,在 application 模塊的 build.gradle 中:

dependencies {
    implementation project(':domain')
    implementation project(':repository')
    implementation 'org.springframework.boot:spring-boot-starter'
}

類似地,在其他模塊中聲明所需的依賴。

3. 配置 SpringBoot

在 api 模塊中,添加 SpringBoot應用的主類:

// api/src/main/java/com/example/api/MyDddProjectApplication.java
package com.example.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.example")
public class MyDddProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyDddProjectApplication.class, args);
    }
}

說明:使用 @SpringBootApplication 注解并設置 scanBasePackages 為 com.example,確保 Spring 能掃描到所有子模塊中的組件。

4. 配置應用屬性

在 api 模塊中創建 src/main/resources/application.properties,并添加必要的配置:

# MySQL 配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# 倉儲選擇
app.repository.type=mysql
# 或者
# app.repository.type=dynamodb

# DynamoDB 配置(如適用)
# aws.accessKeyId=YOUR_ACCESS_KEY
# aws.secretAccessKey=YOUR_SECRET_KEY
# aws.region=us-east-1

5. 構建和運行

在根項目目錄下,運行以下命令以構建和運行應用:

./gradlew build
./gradlew :api:bootRun

確保你的 MySQL 和 DynamoDB(如果選擇使用)都已正確配置和運行。

五、項目優化

1. 使用 Spring Profiles

為了更靈活地在不同環境(如開發、測試、生產)中選擇倉儲實現,可以使用 Spring Profiles。

示例:

在 MySQLUserRepository 和 DynamoDBUserRepository 上添加 @Profile 注解。

// MySQLUserRepository.java
@Repository
@Profile("mysql")
public class MySQLUserRepository implements UserRepository {
    // ...
}

// DynamoDBUserRepository.java
@Repository
@Profile("dynamodb")
public class DynamoDBUserRepository implements UserRepository {
    // ...
}

在 application.properties 中指定活躍配置:

spring.profiles.active=mysql
# 或者
# spring.profiles.active=dynamodb

2. 使用 MapStruct進行對象映射

手動在倉儲實現中轉換領域對象和持久化對象可能繁瑣,使用 MapStruct 可以簡化這一過程。

示例:

添加 MapStruct 依賴(已在 persistence-mysql 模塊中添加)。

定義映射接口:

// infrastructure/persistence-mysql/src/main/java/com/example/infrastructure/persistence/mysql/UserMapper.java
package com.example.infrastructure.persistence.mysql;

import com.example.domain.User;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserEntity toEntity(User user);
    User toDomain(UserEntity entity);
}

在 MySQLUserRepository 中使用 UserMapper 進行轉換:

// MySQLUserRepository.java
package com.example.infrastructure.persistence.mysql;

import com.example.domain.User;
import com.example.repository.UserRepository;
import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Optional;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Profile("mysql")
publicclass MySQLUserRepository implements UserRepository {

    privatefinal JpaUserRepository jpaUserRepository;
    privatefinal UserMapper userMapper = UserMapper.INSTANCE;

    @Autowired
    public MySQLUserRepository(JpaUserRepository jpaUserRepository){
        this.jpaUserRepository = jpaUserRepository;
    }

    @Override
    public void save(User user) {
        UserEntity entity = userMapper.toEntity(user);
        jpaUserRepository.save(entity);
    }

    @Override
    public Optional<User> findById(String id) {
        Optional<UserEntity> entityOpt = jpaUserRepository.findById(id);
        return entityOpt.map(userMapper::toDomain);
    }
}

3. 增加測試模塊

為每個模塊編寫單元測試和集成測試,確保各部分功能正常。

4. 使用依賴注入選擇倉儲實現

在 config 模塊中動態選擇倉儲實現,或使用工廠模式進一步封裝。

六、總結

本文,我們沒有講解 DDD那些燒腦的理論知識,而是按照 DDD 的分層原則詳細地落地了一個user添加和查詢功能,并實現支持多種持久化機制(MySQL 和 DynamoDB)的倉儲層設計。只要你有過 MVC 的 web開發經驗,應該可以快速理解上述 DDD的代碼工程。

對于 DDD模塊化設計帶來的好處,可以總結為以下幾點:

  • 高內聚、低耦合:每個模塊都有明確的職責,模塊之間通過接口進行通信,降低了耦合度。
  • 易于擴展:未來添加新的持久化機制(如 PostgreSQL、Redis 等)時,只需新增相應的基礎設施子模塊,實現倉儲接口即可。
  • 團隊協作:不同團隊或開發者可以并行開發不同模塊,減少沖突和依賴問題。
  • 可維護性:清晰的模塊邊界和職責分離,使得代碼更易于理解和維護。

在實際項目中,我們可根據具體的業務需求靈活地調整模塊劃分和依賴關系,確保架構設計既符合業務需求,又具備良好的技術基礎。

責任編輯:趙寧寧 來源: 猿java
相關推薦

2024-12-09 09:25:30

2019-02-20 10:22:20

監控HDFSHadoop

2025-03-03 10:00:00

2010-06-09 17:00:43

UML試題

2015-01-19 09:37:00

2014-12-17 12:46:17

華為存儲

2022-07-10 20:45:47

React加載動畫庫

2023-03-22 23:23:25

React加載動畫庫

2009-03-03 13:00:00

虛擬化技術vmwarexen

2018-08-02 09:44:35

AIOps實踐數據

2023-10-23 12:31:40

2018-07-25 18:44:35

智能硬件

2025-03-28 09:52:08

CIGo項目

2009-06-23 18:01:45

Ajax框架源代碼

2018-08-31 08:42:48

LinuxUnix實用程序

2020-06-29 09:58:29

微軟算法照片

2023-11-29 07:17:51

微信機器人AI

2014-12-02 10:02:21

Android異步任務

2023-03-03 17:00:00

部署Linux內核
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: xxxxx免费视频 | 男人阁久久| 国产精品一区二区视频 | 亚洲成人精品久久 | 91亚洲国产 | 亚洲国产一区二区三区四区 | 欧美日韩精品一区二区三区四区 | 一区不卡在线观看 | 蜜桃视频在线观看免费视频网站www | 欧美电影网 | av国产精品毛片一区二区小说 | 99热视| 欧美一级片在线观看 | 国产精品久久久久久久久久久久 | 色综合激情| 亚洲国产精品一区二区三区 | 一区二区三区回区在观看免费视频 | 日日夜夜天天 | 精品国产乱码久久久久久88av | 欧美一区二区在线看 | 久久国产亚洲精品 | 国产精品视频免费看 | 91精品国产综合久久国产大片 | 成人国产综合 | 色综合99| 国产精品日韩欧美一区二区三区 | 日韩av三区 | 国内精品久久久久 | 国产成人午夜高潮毛片 | 午夜影院在线观看视频 | 国产欧美一区二区久久性色99 | 欧美日韩视频在线 | 一区二区三区在线 | 中文在线播放 | 精品亚洲一区二区 | 国产在线观看一区二区 | h视频免费在线观看 | 国产不卡视频 | 久久久精品综合 | 精品美女 | 久久精品亚洲成在人线av网址 |