《柒柒架构》DDD领域驱动设计–领域模型(三)

前言

在前两篇文章中,我们探讨了领域模型的基本概念以及仓储模型的实现。在深入了解仓储的使用之前,我们先来探讨工厂模型这一重要概念。

工厂模型

在开始学习工厂模式之前,建议先熟悉几种创建型设计模式,包括单例、原型、建造者、工厂方法和抽象工厂模式。

DDD实体在不同阶段的定义

在领域驱动设计(DDD)中,实体的定义会随着业务逻辑的深入而发生变化。了解这些变化对于设计和实现领域模型至关重要。

工厂模式

工厂模式是一种设计模式,用于创建对象,而不需要指定将要创建的对象的确切类。它有助于提高系统的灵活性和可扩展性。

DTO与DO的转换

数据传输对象(DTO)和领域对象(DO)之间的转换是实现领域层与表示层解耦的关键。

仓储的使用

仓储是领域模型中用于数据访问的组件,它封装了对聚合根的访问逻辑。

SpringAOP与AspectJ

SpringAOP和AspectJ是两种面向切面编程的实现方式,它们可以用于实现如事务管理、日志记录等横切关注点。

小结

本篇文章对DDD中的工厂模型和仓储使用进行了介绍,为读者提供了深入理解领域驱动设计的理论基础和实践应用。
在这里插入图片描述

由上图可以看出,在各阶段,需要对业务实体数据进行相应的转换,以保证各个层级的内部自洽性,及本层及其下层,应该不依赖上层相关类的定义。
几个概念,不同的人及书籍对DDD的几个概念描述都不尽一致,在本文中,使用如下描述:
DO:领域层对象,包含聚合、实体、值对象
Aggregate:聚合
Aggregate root:聚合根
Entity:实体
VO(value object):值对象
DTO:数据转换对象,主要用于用户接口层对外的数据传输
PO(persistent object):持久化对象
AclDto:防腐层数据传输对象
AclPO:防腐层持久化对象
QUERY:读写分离模式的外部只读请求
COMMAND: 读写分离式的外部写入请求
我们应该认清各个概念所处的层级,以及在各层级进行数据传输时,该进行怎样的数据转换。各层严格来说应该仅可使用该层的概念,比如在领域层不应该出现PO类型的对象,PO应属于基础设施层。

工厂模式

我们要将各层之间数据转换交由工厂中进行实现,通过工厂封装具体的对象转换逻辑,避免将不同层级的对象在不属于该层级的地方出现。
从上图看出,我们需要在多处创建工厂,执行不同层级的数据转换。
最简单的工厂实现如下:(例子来源网络)

public class ApproverAssembler {

    public static ApproverDTO toDTO(Approver approver){
        ApproverDTO dto = new ApproverDTO();
        dto.setPersonId(approver.getPersonId());
        dto.setPersonName(approver.getPersonName());
        return dto;
    }

    public static Approver toDO(ApproverDTO dto){
        Approver approver = new Approver();
        approver.setPersonId(dto.getPersonId());
        approver.setPersonName(dto.getPersonName());
        return approver;
    }

}

《柒柒架构》提供了通用的数据转换工具,且已经在底层实现,我们先看下如何实现的。以PO工厂为例:
仓储实现持久化时进行转换:

 @Override
    public void onInsert(Entity entity, Class c) {
        //1.获取DO对应的Mapper对象
        BaseMapper mapper = getMapper(c);
        //2.转换成相应的DO
        Po aPo = PoAssemblerFactory.toPo(entity);
        //3.执行Mapper处理
        mapper.insert(aPo);
    }

我们再来看下PoAssemblerFactory的实现:

public interface PoAssemblerFactory {

    static Po toPo(Entity entity) {
        PoAssembleUtil poAssembleUtil = SpringContextUtil.getBean(PoAssembleUtil.class);
        Po po = null;
        try {
            po = poAssembleUtil.toPo(entity);
        } catch (Exception e) {
            String poPackage = SpringContextUtil.getProperty("actable.model.pack");
            String poClassName = poPackage + entity.getClass().getSimpleName() + "Po";
            Class poClass = null;
            try {
                poClass = Class.forName(poClassName);
                return (Po) PropertiesCopyUtil.deepCopyProperties(entity, poClass);
            } catch (ClassNotFoundException e1) {
                e.printStackTrace();
            }
        }
        return po;
    }

    static Entity toEntity(Po po, Class c) {
        PoAssembleUtil poAssembleUtil = SpringContextUtil.getBean(PoAssembleUtil.class);
        Entity entity = null;
        try {
            entity = poAssembleUtil.toEntity(po, c);
        } catch (Exception e) {
            PropertiesCopyUtil.deepCopyProperties(po, c);
        }
        return entity;
    }

}

可以看出,工厂类先去使用PoAssembleUtil进行转换,转换失败则会使用PropertiesCopyUtil对象进行转换。(PropertiesCopyUtil是自己定义的可以深度进行属性复制的工具类,下面贴下部分代码)

@Slf4j
public class PropertiesCopyUtil {

    public static <T> T deepCopyProperties(Object source, Class<T> targetClass) {
        if (Map.class.isAssignableFrom(targetClass)) {
            return (T) toFlattenMap(source, null);
        } else {
            Map map = toFlattenMap(source, null);
            return toDeepCopyProperties(map, targetClass);
        }
    }

    @SneakyThrows
    private static <T> T fromDeepCopyProperties(Object source, Class<T> targetClass) {
        T target = targetClass.newInstance();
        fromCopy(source, target);
        return target;
    }

    @SneakyThrows
    private static <T> T toDeepCopyProperties(Object source, Class<T> targetClass) {
        T target = targetClass.newInstance();
        toCopy(source, target);
        return target;
    }
}

下面我们看下PoAssembleUtil代码:

public class PoAssembleUtil {

    private Map<String, Method> methodMap = new HashMap<>();

    @SneakyThrows
    public Po toPo(Entity o) {
        Class c = o.getClass();
        Object poAssemble = getPoAssemble(c);
        if (poAssemble == null) {
            throw new Exception("未自定义" + c.getSimpleName() + "PO转化类!");
        }
        if (poAssemble instanceof PoAssemble) {
            return ((PoAssemble) poAssemble).toPo(o);
        } else {
            Method toDoMethod = methodMap.get(c.getSimpleName() + "toPo");
            if (toDoMethod == null) {
                Method[] methods = poAssemble.getClass().getMethods();
                for (Method m : methods) {
                    if (m.getName().equals("toPo")) {
                        methodMap.put(c.getSimpleName() + "toPo", m);
                        toDoMethod = m;
                        break;
                    }
                }
            }
            Object value = toDoMethod.invoke(poAssemble, o);
            return (Po) value;
        }
    }

    @SneakyThrows
    public Entity toEntity(Po o, Class c) {
        Object poAssemble = getPoAssemble(c);
        if (poAssemble == null) {
            throw new Exception("未自定义" + c.getSimpleName() + "PO转化类!");
        }
        if (poAssemble instanceof PoAssemble) {
            return ((PoAssemble) poAssemble).toEntity(o);
        } else {
            Method toDoMethod = methodMap.get(c.getSimpleName() + "toEntity");
            if (toDoMethod == null) {
                Method[] methods = poAssemble.getClass().getMethods();
                for (Method m : methods) {
                    if (m.getName().equals("toEntity")) {
                        methodMap.put(c.getSimpleName() + "toEntity", m);
                        toDoMethod = m;
                        break;
                    }
                }
            }
            Object value = toDoMethod.invoke(poAssemble, o);
            return (Entity) value;
        }
    }

    private Object getPoAssemble(Class c) {
        String className = c.getSimpleName();
        String mapperName = className.substring(0, 1).toLowerCase().concat(className.substring(1)) + "PoAssemblerImpl";
        return SpringContextUtil.getBean(mapperName);
    }

}

该工具类会先去Spring容器中查找有无名为“${实体ID}PoAssemblerImpl”的bean,如果找到的话,则会调用该bean对应的toPo或者toEntity方法。
这边建议使用MapStruct工具对不同类型的对象进行转换,因为Mapstruct在转换上相对于其他工具有着更高的效率,且可以通过注解完成栏位的映射配置。如果字段一一对应,可无需进行Mapping映射。
下面是使用Mapstruct进行转换的例子:

@Mapper(componentModel = "spring") //使用通用处理仓库时,必须使用spring管理
// 注意这里,在使用通用处理仓库对象处理时,必须继承DomainDoAssemble接口
public interface CustomInfoPoAssembler {
    //引用静态方式时,必须要要有此属性
    //使用spring注入时,可以不需要。使用通用仓库对象时,必须使用spring管理,这个可以不需要
    CustomInfoPoAssembler INSTANCES = Mappers.getMapper(CustomInfoPoAssembler.class);

    @Mappings({@Mapping(target = "userId", source = "user.userId"), @Mapping(target = "userName", source = "user.userName"),
            @Mapping(target = "positionId", source = "position.positionId"), @Mapping(target = "positionName", source = "position.positionName")})
    CustomInfoPo toPo(CustomInfo customInfo);
    // 如果字段没有不一致,一行注解都不需要

    @Mappings({@Mapping(source = "userId", target = "user.userId"), @Mapping(source = "userName", target = "user.userName"),
            @Mapping(source = "positionId", target = "position.positionId"), @Mapping(source = "positionName", target = "position.positionName")})
    CustomInfo toEntity(CustomInfoPo customInfoDo);
}

DTO与DO转换

在软件开发中,DTO(Data Transfer Object)和DO(Domain Object)是两种常见的对象类型。DTO通常用于在用户接口层与应用层之间传输数据,而DO则代表领域模型对象。当数据从用户界面或第三方应用流入应用层时,需要将DTO转换为DO对象。由于存在多种DTO类型,转换过程可能会相对复杂。以下是两种转换方式的建议:

  1. 手动转换:开发者可以根据DTO和DO的结构,手动编写转换逻辑。2. 使用工具类:可以利用《柒柒架构》提供的PropertiesCopyUtil工具类来简化对象转换过程。

工厂模式与仓储模式

在介绍仓储模式之前,我们先回顾一下工厂模式。工厂模式是一种创建型设计模式,用于创建对象,而不需要指定将要创建的对象的具体类。接下来,我们将探讨仓储模式的应用。

仓储模式

仓储模式是一种设计模式,用于在应用程序中提供数据访问对象(Repository)的抽象,允许开发者以一种统一的方式访问数据源。

Spring AOP与AspectJ

在深入仓储模式之前,我们先来了解Spring AOP(面向切面编程)和AspectJ。Spring AOP在Spring Boot应用启动时,对bean进行动态代理,主要特点如下:

  1. Spring管理的对象:只有那些交由Spring管理的对象才能被动态代理。2. 不适合代理的对象:有些对象,如在线程中使用后立即销毁的实体对象,不适合由Spring管理。3. 聚合对象:聚合对象通常属于不适合由Spring管理的范畴。

AspectJ代理

AspectJ是一个面向切面的编程框架,它允许开发者以声明方式添加额外功能,如日志记录、事务管理等,而不需要修改业务逻辑代码。

结语

通过上述内容,我们了解了DTO与DO的转换方法,工厂模式和仓储模式的概念,以及Spring AOP和AspectJ的基本知识。这些知识点对于构建高效、可维护的应用程序至关重要。

public @interface LogAnnotation {
    String value() default "INFO";
}
@Aspect
@Component
public class LogAop {
    private final String strDateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSS";
    private final SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);

    @Pointcut("@annotation(com.cmbchina.ns.casesystem.common.infrastructure.utils.logaop.LogAnnotation)")
    public void logAspect() {
    }

    @Around("logAspect()")
    public Object infoLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            Object[] querys = joinPoint.getArgs();
            String queryStr = Arrays.stream(querys).map(x -> x.toString()).reduce(new BinaryOperator<String>() {
                @Override
                public String apply(String s, String s2) {
                    return s + "," + s2;
                }
            }).get();
            log.info("开始执行时间:" + sdf.format(startTime) + ",请求参数:" + queryStr + "。");
            Object o = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            log.info("结束执行时间:" + sdf.format(endTime) + ",执行时长:" + duration + ",返回值:" + Optional.ofNullable(o).toString() + "。");
            return o;
        } catch (Exception t) {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            log.error("执行异常时间:" + sdf.format(endTime) + ",执行时长:" + duration + ",错误原因:" + t.getMessage() + "。");
            t.printStackTrace();
            throw t;
        }

    }
}

在Java开发中,动态代理是一种常用的技术,Spring框架和Spring Boot都提供了对动态代理的支持。以下是对动态代理技术及其在Spring AOP和AspectJ中的实现方式的详细解析:

动态代理概述动态代理是一种在运行时动态创建代理对象的技术,它允许开发者在不修改原有代码的情况下,增加额外的功能。

Spring AOP的动态代理Spring AOP使用的是代理模式,通过创建目标对象的代理来实现AOP的功能。Spring AOP的代理是基于接口的,因此目标对象必须实现一个接口。

AspectJ的动态代理与Spring AOP不同,AspectJ使用的是字节码操作技术,它在编译时对字节码进行增强。AspectJ的代码没有Spring框架的约束,可以独立于Spring使用。

AspectJ的配置要在项目中使用AspectJ,需要在pom.xml文件中添加AspectJ的相关依赖,在编译时指定AspectJ的配置文件。

代码实现具体的实现代码展示了如何使用AspectJ注解来定义切面和切点。

注意事项- AspectJ和Lombok可能会存在冲突。当使用AspectJ时,IDEA默认使用Ajc进行编译,但可以通过设置更改为使用javac编译来解决这个问题。

结论动态代理是Java开发中一个强大的功能,Spring AOP和AspectJ提供了两种不同的实现方式,开发者可以根据项目需求选择适合的实现。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.12</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.11</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>1.9.4</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>1.9.4</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>1.9.4</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <complianceLevel>${java.version}</complianceLevel>
                    <aspectLibraries>
                        <aspectLibrary>
                            <artifactId>common</artifactId>
                            <groupId>{这里是你的工程}</groupId>
                        </aspectLibrary>
                    </aspectLibraries>
                    <aspectDirectory>
                        {这里是你的切面对象}
                    </aspectDirectory>
                    <forceAjcCompile>true</forceAjcCompile>
                    <sources/>
                    <weaveDirectories>
                        <weaveDirectory>${project.build.outputDirectory}</weaveDirectory>
                    </weaveDirectories>
                </configuration>
            </plugin>

由于我们的DO聚合对象都是被new出来的,是不适合被Spring管理的,所以我们选择使用AspectJ进行动态代理,我们需要将聚合对象无感知的进行仓储方法的执行。
那先看下,我们是如何对其进行代理的。

package com.cmbchina.ns.casesystem.common.domain.aspectjaop;

import com.cmbchina.ns.casesystem.common.domain.Aggregate;
import com.cmbchina.ns.casesystem.common.domain.Application;
import com.cmbchina.ns.casesystem.common.domain.repository.BaseRepository;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/***
 * @author by zhoufankun
 * @date 2021/07/26
 */
@Aspect
@Slf4j
public class AggregateEntityAspectjAop {

    @Pointcut("execution(* com.cmbchina.ns.casesystem.common.domain.Aggregate+.*(..)) " +
            "&& !execution(* get*(..)) && !execution(* toString(..)) " +
            "&& !execution(* hashCode(..)) && !execution(* canEqual(..)) && !execution(* equals(..))" +
            "&& !execution(* builder(..)) && !execution(* idValue(..)) && !execution(* idKey(..))")
    public void aggregateAspect() {
    }

    @SneakyThrows
    @Around("aggregateAspect()")
    public Object aroundAggregate(ProceedingJoinPoint joinPoint) {
        StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
        Object o = null;
        if (stackTraceElement.getClassName().startsWith("sun.reflect.GeneratedMethodAccessor")) {
            return joinPoint.proceed();
        }
        if (Application.class.isAssignableFrom(Class.forName(stackTraceElement.getClassName()))) {
            o = joinPoint.proceed();
            Aggregate aggregate = (Aggregate) joinPoint.getTarget();
            Class c = aggregate.getClass();
            BaseRepository.save(aggregate, c);
        } else {
            o = joinPoint.proceed();
        }
        return o;
    }

    @Pointcut("execution(* com.cmbchina.ns.casesystem.common.domain.Aggregate+.*(..)) " +
            "&& execution(* get*(..)) ")
    public void aggregateGetMethodAspect() {
    }

    @SneakyThrows
    @Around("aggregateGetMethodAspect()")
    public Object aroundAggregateGetMethod(ProceedingJoinPoint joinPoint) {
        StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
        Object o = null;
        if (stackTraceElement.getClassName().startsWith("sun.reflect.GeneratedMethodAccessor")) {
            return joinPoint.proceed();
        }
        if (Application.class.isAssignableFrom(Class.forName(stackTraceElement.getClassName()))) {
            o = joinPoint.proceed();
            if (Aggregate.class.isAssignableFrom(o.getClass())) {
                long threadId = Thread.currentThread().getId();
                Aggregate aggregate = (Aggregate) joinPoint.getTarget();
                Class c = aggregate.getClass();
                ThreadLocalAggregateAopUtil.setAggregate(threadId, aggregate);
                ThreadLocalAggregateAopUtil.setAggregateClass(threadId, c);
            }
        } else {
            o = joinPoint.proceed();
        }
        return o;
    }

    @Pointcut("execution(* com.cmbchina.ns.casesystem.common.domain.Entity+.*(..)) " +
            "&& !execution(* get*(..)) && !execution(* toString(..)) " +
            "&& !execution(* hashCode(..)) && !execution(* canEqual(..)) && !execution(* equals(..))" +
            "&& !execution(* builder(..)) ")
    public void entityAspect() {
    }

    @SneakyThrows
    @Around("entityAspect()")
    public Object aroundEntity(ProceedingJoinPoint joinPoint) {
        StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
        Object o = null;
        if (stackTraceElement.getClassName().startsWith("sun.reflect.GeneratedMethodAccessor")) {
            return joinPoint.proceed();
        }
        if (Application.class.isAssignableFrom(Class.forName(stackTraceElement.getClassName()))) {
            long threadId = Thread.currentThread().getId();
            o = joinPoint.proceed();
            Aggregate aggregate = ThreadLocalAggregateAopUtil.getAggregate(threadId);
            Class c = ThreadLocalAggregateAopUtil.getAggregateClass(threadId);
            BaseRepository.save(aggregate, c);
        } else {
            o = joinPoint.proceed();
        }
        return o;
    }

}

在Spring框架中,Lombok是一个常用的库,它可以通过注解自动生成getters、setters等方法,从而简化代码。然而,我们通常希望对某些方法进行代理,而排除Lombok生成的方法。以下是对上述需求的详细解析:

  1. 方法代理规则:我们希望排除Lombok生成的方法和以get开头的方法,不对这些方法进行代理。
  2. 调用检查:我们需要检查对聚合或实体的调用是否源自Application对象。如果是,我们通过调用仓储(Repository)的方法来进行保存操作;如果不是,则不进行任何处理。
  3. 事务管理:从Application对象发起的调用将触发事务操作,而聚合内部的相互调用则不会启动事务。
  4. 简化操作:在聚合和实体的业务方法中,开发者无需显式定义BaseRepository.save()方法,即可完成仓储操作。 接下来,我们看看BaseRepository的实现细节。
package com.cmbchina.ns.casesystem.common.domain.repository;

import com.cmbchina.ns.casesystem.common.domain.Aggregate;
import com.cmbchina.ns.casesystem.common.domain.busivalid.DddValidationManager;
import com.cmbchina.ns.casesystem.common.infrastructure.utils.SpringContextUtil;
import lombok.SneakyThrows;

import java.util.List;

/***
 * 继承该抽象类,可以实现自动获取对应Aggregate对象对应的仓储对象。
 * 仓储对象命名规范: “{aggregate}RepositoryImpl”
 * @auther by zhoufankun
 * @date 2021/7/30
 */
public interface BaseRepository {
    /**
     * @param id 传入的聚合根ID
     * @param c  传入的聚合class类型
     * @return 返回查到的聚合结果
     */
    @SneakyThrows
    static <T extends Aggregate> T find(Object id, Class<T> c) {
        String className = c.getSimpleName();
        Repository repository = (Repository) SpringContextUtil.getBean(className.substring(0, 1).toLowerCase().concat(className.substring(1)) + "RepositoryImpl");
        if (repository == null) {
            repository = (Repository) SpringContextUtil.getBean("generalRepositoryImpl");
        }
        return (T) repository.find(id, c);
    }

    /**
     * @param id 传入的聚合根ID
     * @param c  传入的聚合class类型
     */
    @SneakyThrows
    static <T extends Aggregate> void remove(Object id, Class<T> c) {
        String className = c.getSimpleName();
        Repository repository = (Repository) SpringContextUtil.getBean(className.substring(0, 1).toLowerCase().concat(className.substring(1)) + "RepositoryImpl");
        if (repository == null) {
            repository = (Repository) SpringContextUtil.getBean("generalRepositoryImpl");
        }
        repository.remove(id, c);
    }

    /**
     * @param aggregate,c 传入的聚合class类型
     */
    @SneakyThrows
    static <T extends Aggregate> void save(Aggregate aggregate, Class<T> c) {
        DddValidationManager dddValidationManager = SpringContextUtil.getBean(DddValidationManager.class);
        List<DddValidationManager.ValidResult> valid = dddValidationManager.valid(aggregate);
        if (valid == null || valid.size() == 0) {
            String className = c.getSimpleName();
            Repository repository = (Repository) SpringContextUtil.getBean(className.substring(0, 1).toLowerCase().concat(className.substring(1)) + "RepositoryImpl");
            if (repository == null) {
                repository = (Repository) SpringContextUtil.getBean("generalRepositoryImpl");
            }
            repository.save(aggregate, c);
        } else {
            throw new Exception(valid.toString());
        }

    }

}

我们看出,BaseRepository是我们操作仓储的基础工具,无需显示使用其他仓储实现类。

  • 如果应用中有定义“${聚合ID}RepositoryImpl”,我们则会使用该实现进行仓储的处理,如果没有显示定义,我们则会使用通用的generalRepositoryImpl bean进行仓储操作,一般情况下,使用通用仓储即可满足。
    我们看看具体使用:
@Service
public class CustomInfoApplication implements Application {

    @Autowired
    private CustomInfoService customInfoService;

    public CustomInfo query(String id) {
        return BaseRepository.find(id, CustomInfo.class);
    }

    public void insert(CustomInfo customInfo) {
        BaseRepository.save(customInfo, CustomInfo.class);
    }

    public void delete(String id) {
        BaseRepository.remove(id, CustomInfo.class);
    }

    public void update(CustomInfo customInfo) {
        BaseRepository.save(customInfo, CustomInfo.class);
    }

    @EventDrivenPattern("transCash.success")
    public void doSomething(String id) {
        CustomInfo customInfo = BaseRepository.find(id, CustomInfo.class);
        customInfo.handle1();
    }

    public void transCash(int cashMoney, String id1, String id2) {
        CustomInfo customInfo1 = BaseRepository.find(id1, CustomInfo.class);
        CustomInfo customInfo2 = BaseRepository.find(id2, CustomInfo.class);
        customInfoService.transCash(cashMoney, customInfo1, customInfo2);
    }
}

在大多数情况下,除了插入和删除操作需要显示地使用BaseRepository进行处理之外,其他相关业务操作用户无需显式执行。这是因为在没有聚合对象的情况下,应用程序不可能直接对聚合本身进行操作,因此获取聚合对象必须通过BaseRepository类来实现。用户在完成业务操作后,代理对象会自动执行BaseRepository.save()方法。 通过查看代理对象的实现,我们可以发现其具体细节。

StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
if (Application.class.isAssignableFrom(Class.forName(stackTraceElement.getClassName()))) {
xxxxxx
}

在软件开发中,事务处理是一个关键的概念,特别是在领域驱动设计(Domain-Driven Design, DDD)中。事务确保了业务操作的完整性和一致性。以下是对事务处理和领域层仓储模型的详细阐述:

事务处理的重要性事务处理是确保数据一致性的重要机制。在DDD中,只有当从应用层调用聚合或实体的业务方法时,才会触发事务。这意味着,如果聚合内部互相调用,不会触发新的事务,从而保证了业务操作的事务性。

领域层仓储模型领域层的仓储模型是DDD架构中的核心组成部分。它负责存储和检索聚合根。通过使用仓储模型,可以简化数据访问逻辑,使开发者能够更专注于业务逻辑的实现。

低代码实现《柒柒架构》在实现低代码开发方面做出了贡献。它通过减少架构使用者需要编写的底层代码,使得开发者可以更加专注于业务逻辑,从而提高开发效率。

仓储模型的实现与使用理解仓储模型的实现和使用,对于开发者来说非常关键。即使不采用DDD思想,掌握仓储模型也能带来诸多好处,比如简化数据访问和提高代码的可维护性。

小结本文对领域层的仓储模型进行了深入探讨,揭示了其在DDD架构中的核心地位以及如何通过《柒柒架构》实现低代码开发,从而减少底层代码的开发工作。