域对象(Domain Object)在Java Web开发中的应用 - 实践

域对象(Domain Object)在Java Web开发中的应用

什么是域对象?

域对象(Domain Object),也称为领域对象或实体对象,是面向对象编程中用于表示业务领域中的实体和概念的对象。它们封装了业务数据和行为,是业务模型的核心组成部分。

域对象的类型

在Java Web开发中,主要有四种类型的域对象:

  1. 普通JavaBean:简单的数据载体
  2. 实体类:与数据库表映射的对象
  3. 数据传输对象(DTO):在不同层之间传输数据的对象
  4. 值对象:表示不可变数据的对象

域对象的最佳实践

1. 基本域对象设计

以下是一个良好的域对象设计示例:

package com.xie.oa.domain;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 部门领域对象
* 代表业务领域中的部门实体
*/
public class Dept implements Serializable {
private static final long serialVersionUID = 1L;
// 基本属性
private Integer deptno;      // 部门编号
private String dname;        // 部门名称
private String loc;          // 部门位置
// 审计字段
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String createdBy;
private String updatedBy;
// 状态字段
private Boolean active;
// 构造方法
public Dept() {
this.active = true;
this.createTime = LocalDateTime.now();
}
public Dept(Integer deptno, String dname, String loc) {
this();
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
// Getter和Setter方法
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
// 业务方法
public void deactivate() {
this.active = false;
this.updateTime = LocalDateTime.now();
}
public void activate() {
this.active = true;
this.updateTime = LocalDateTime.now();
}
// 重写equals和hashCode方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dept dept = (Dept) o;
return Objects.equals(deptno, dept.deptno);
}
@Override
public int hashCode() {
return Objects.hash(deptno);
}
// 重写toString方法
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
", active=" + active +
'}';
}
// 验证方法
public boolean isValid() {
return deptno != null &&
dname != null && !dname.trim().isEmpty() &&
loc != null && !loc.trim().isEmpty();
}
// 构建器模式(可选)
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Integer deptno;
private String dname;
private String loc;
public Builder deptno(Integer deptno) {
this.deptno = deptno;
return this;
}
public Builder dname(String dname) {
this.dname = dname;
return this;
}
public Builder loc(String loc) {
this.loc = loc;
return this;
}
public Dept build() {
Dept dept = new Dept();
dept.setDeptno(deptno);
dept.setDname(dname);
dept.setLoc(loc);
return dept;
}
}
}

2. 使用域对象的Service层示例

package com.xie.oa.service;
import com.xie.oa.domain.Dept;
import java.util.List;
import java.util.Optional;
/**
* 部门服务接口
*/
public interface DeptService {
/**
* 根据ID查找部门
*/
Optional<Dept> findById(Integer deptno);
  /**
  * 查找所有部门
  */
  List<Dept> findAll();
    /**
    * 查找所有活跃部门
    */
    List<Dept> findAllActive();
      /**
      * 保存部门
      */
      Dept save(Dept dept);
      /**
      * 更新部门
      */
      Dept update(Dept dept);
      /**
      * 根据ID删除部门(逻辑删除)
      */
      void deleteById(Integer deptno);
      /**
      * 检查部门名称是否已存在
      */
      boolean existsByDname(String dname);
      }

3. 服务实现

package com.xie.oa.service.impl;
import com.xie.oa.domain.Dept;
import com.xie.oa.repository.DeptRepository;
import com.xie.oa.service.DeptService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class DeptServiceImpl implements DeptService {
private final DeptRepository deptRepository;
public DeptServiceImpl(DeptRepository deptRepository) {
this.deptRepository = deptRepository;
}
@Override
@Transactional(readOnly = true)
public Optional<Dept> findById(Integer deptno) {
  return deptRepository.findById(deptno);
  }
  @Override
  @Transactional(readOnly = true)
  public List<Dept> findAll() {
    return deptRepository.findAll();
    }
    @Override
    @Transactional(readOnly = true)
    public List<Dept> findAllActive() {
      return deptRepository.findByActiveTrue();
      }
      @Override
      public Dept save(Dept dept) {
      if (!dept.isValid()) {
      throw new IllegalArgumentException("部门数据不完整");
      }
      if (existsByDname(dept.getDname())) {
      throw new IllegalStateException("部门名称已存在");
      }
      return deptRepository.save(dept);
      }
      @Override
      public Dept update(Dept dept) {
      if (!dept.isValid()) {
      throw new IllegalArgumentException("部门数据不完整");
      }
      return deptRepository.findById(dept.getDeptno())
      .map(existingDept -> {
      // 检查名称是否与其他部门冲突
      if (!existingDept.getDname().equals(dept.getDname()) &&
      existsByDname(dept.getDname())) {
      throw new IllegalStateException("部门名称已存在");
      }
      existingDept.setDname(dept.getDname());
      existingDept.setLoc(dept.getLoc());
      existingDept.setUpdateTime(java.time.LocalDateTime.now());
      return deptRepository.save(existingDept);
      })
      .orElseThrow(() -> new IllegalArgumentException("部门不存在"));
      }
      @Override
      public void deleteById(Integer deptno) {
      deptRepository.findById(deptno)
      .ifPresent(dept -> {
      dept.deactivate();
      deptRepository.save(dept);
      });
      }
      @Override
      @Transactional(readOnly = true)
      public boolean existsByDname(String dname) {
      return deptRepository.existsByDnameAndActiveTrue(dname);
      }
      }

4. 使用构建器模式创建域对象

// 使用构建器模式创建Dept对象
Dept dept = Dept.builder()
.deptno(10)
.dname("技术部")
.loc("北京")
.build();
// 或者使用构造函数
Dept dept = new Dept(10, "技术部", "北京");

域对象的设计原则

  1. 高内聚低耦合:域对象应该包含相关的数据和行为,减少对外部依赖
  2. 封装性:隐藏内部实现细节,通过公共方法暴露功能
  3. 不可变性:在可能的情况下,使对象不可变以提高线程安全性
  4. 有效性:域对象应该能够自我验证其状态的有效性
  5. 业务逻辑:将属于该领域对象的业务逻辑放在域对象中

域对象在Web层中的使用

1. Controller中使用域对象

@RestController
@RequestMapping("/api/depts")
public class DeptController {
private final DeptService deptService;
public DeptController(DeptService deptService) {
this.deptService = deptService;
}
@GetMapping("/{deptno}")
public ResponseEntity<Dept> getDept(@PathVariable Integer deptno) {
  return deptService.findById(deptno)
  .map(ResponseEntity::ok)
  .orElse(ResponseEntity.notFound().build());
  }
  @PostMapping
  public ResponseEntity<Dept> createDept(@Valid @RequestBody Dept dept) {
    try {
    Dept savedDept = deptService.save(dept);
    return ResponseEntity.created(URI.create("/api/depts/" + savedDept.getDeptno()))
    .body(savedDept);
    } catch (IllegalArgumentException e) {
    return ResponseEntity.badRequest().build();
    } catch (IllegalStateException e) {
    return ResponseEntity.status(HttpStatus.CONFLICT).build();
    }
    }
    @PutMapping("/{deptno}")
    public ResponseEntity<Dept> updateDept(
      @PathVariable Integer deptno,
      @Valid @RequestBody Dept dept) {
      if (!deptno.equals(dept.getDeptno())) {
      return ResponseEntity.badRequest().build();
      }
      try {
      Dept updatedDept = deptService.update(dept);
      return ResponseEntity.ok(updatedDept);
      } catch (IllegalArgumentException e) {
      return ResponseEntity.notFound().build();
      } catch (IllegalStateException e) {
      return ResponseEntity.status(HttpStatus.CONFLICT).build();
      }
      }
      @DeleteMapping("/{deptno}")
      public ResponseEntity<Void> deleteDept(@PathVariable Integer deptno) {
        deptService.deleteById(deptno);
        return ResponseEntity.noContent().build();
        }
        }

2. JSP页面中使用域对象

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>



    部门详情


    

部门详情

部门编号 ${dept.deptno}
部门名称 ${dept.dname}
部门位置 ${dept.loc}
状态 ${dept.active ? '活跃' : '已禁用'}
创建时间 ${dept.createTime}

部门不存在

返回列表

域对象与数据转换

1. DTO模式

对于复杂的业务场景,可以使用DTO(Data Transfer Object)来优化数据传输:

/**
* 部门DTO,用于前后端数据传输
*/
public class DeptDTO {
private Integer deptno;
private String dname;
private String loc;
// 构造方法、Getter和Setter
/**
* 从领域对象转换为DTO
*/
public static DeptDTO fromDomain(Dept dept) {
DeptDTO dto = new DeptDTO();
dto.setDeptno(dept.getDeptno());
dto.setDname(dept.getDname());
dto.setLoc(dept.getLoc());
return dto;
}
/**
* 从DTO转换为领域对象
*/
public Dept toDomain() {
Dept dept = new Dept();
dept.setDeptno(this.deptno);
dept.setDname(this.dname);
dept.setLoc(this.loc);
return dept;
}
}

2. 使用MapStruct进行对象映射

对于大型项目,可以使用MapStruct等工具简化对象转换:

@Mapper(componentModel = "spring")
public interface DeptMapper {
DeptDTO deptToDeptDTO(Dept dept);
Dept deptDTOToDept(DeptDTO deptDTO);
}

总结

域对象是Java Web开发中的核心概念,良好的域对象设计可以提高代码的可维护性、可测试性和可扩展性。设计域对象时应遵循面向对象的原则,封装业务数据和行为,提供适当的验证方法,并考虑线程安全性和不可变性。

在实际项目中,域对象通常与Service层、Repository层和Web层协同工作,形成清晰的分层架构。通过合理使用DTO模式和对象映射工具,可以进一步优化系统性能和提高开发效率。

posted @ 2025-10-24 10:42  ycfenxi  阅读(2)  评论(0)    收藏  举报