Introduction: In Java program development, naming and application layering are undoubtedly the two “pain points” of the majority of backend compatriots. This paper provides a lightweight application layering structure design based on domain model for your reference. The following sections are introduced in terms of hierarchical structure, hierarchical details, invocation relationships, layer specifications and common code tools.

The author | | ali AZhuo source technology public number

The preface

In Java program development, naming and application layering are undoubtedly the two “pain points” of the vast backend compatriots. This paper provides a lightweight application layering structure design based on domain model for your reference. The following sections are introduced in terms of hierarchical structure, hierarchical details, invocation relationships, layer specifications and common code tools.

A layered structure

  • Web (Front-end request layer)

Requests from the front end are processed by invoking business layer services.

  • Biz (Business layer)

Provide encapsulated capabilities that are assembled and choreographed for business logic processing.

  • Dal (Data Layer)

Add, delete, change and check the underlying data source.

  • Client (External Request Layer)

Define interfaces that are exposed to other applications.

  • Common (External Common Layer)

Defines the public classes exposed to the outside world.

  • Facade

Requests from external applications are processed by invoking business layer services.

Two layer detail

Web (Front-end request layer)

Biz (Business layer)

Dal (Data Layer)

Client (External Request Layer)

Common (External Common Layer)

Facade

Start (start class)

Qatest (Test class)

Three Call relation

Note:

  • Services and services can call each other directly;
  • The domain capability that a service can invoke multiple domains;
  • Domain capability is the capability of the minimum granularity encapsulated, which cannot be called each other.
  • Query services call manager directly without invoking domain capabilities;

Four layer specifications

Web (Front-end request layer)

  • Define a unified exception handling aspect: handle business exceptions and other runtime exceptions;

Biz (Business layer)

  • The internal service does not do exception handling and returns the Result wrapper class; exceptions are thrown at the Web layer and facade layer.
  • Query services are separated from other services and placed in a separate package;
  • A capability that uniquely corresponds to a domain and is the minimum granularity of the encapsulated capability.
  • External services should be handled and encapsulated in remote.
  • The common class in the business layer is a common class that is used only within the application.

Dal (Data Layer)

  • Mapper is stored separately for different types of data sources, such as ADB and XDB.

Common (External Common Layer)

  • Common stores only exposed entity classes, constants, and enumerations;
  • Dtos exposed to the external are reserved only for externally necessary fields, while other fields such as feature cannot exist.

Facade

  • Define a unified exception handling aspect: handle business exceptions and other runtime exceptions;
  • The HSF implementation class at the facade layer does simple parameter validation and conversion, and does not write business logic.

General code and tools

Web (Front-end request layer)

  • Unified exception handling facets

    @RestControllerAdvice public class RestExceptionHandler {

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public Result system(HttpServletRequest req, Exception e) {
        AllLoggers.EXCEPTION.error("RestExceptionHandler.system|servlet:{}|method:{}|code:{}|msg:{}",
                req.getServletPath(),req.getMethod(), e.getMessage(), e);
        return Result.error(ResultCode.BASE.SYSTEM_ERROR);
    }
    
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BusinessException.class)
    public Result business(HttpServletRequest req, BusinessException e) {
        AllLoggers.EXCEPTION.error("RestExceptionHandler.business|servlet:{}|method:{}|code:{}|msg:{}",
                req.getServletPath(),req.getMethod(), e.getMessage(), e);
        return Result.error(e.getErrorCode(), e.getErrorMessage());
    }
    Copy the code

    }

Biz (Business layer)

  • Unified log printing utility class

    import org.slf4j.Logger; import org.slf4j.LoggerFactory;

    public interface AllLoggers {

    /** * APPLICATION = loggerFactory.getLogger ("APPLICATION"); /** * Logger EXCEPTION = LoggerFactory.getLogger("EXCEPTION"); /** * BIZ = loggerFactory.getLogger ("BIZ"); /** * Logger HSF = loggerFactory.getLogger ("HSF"); MTOP = loggerFactory.getLogger ("MTOP");Copy the code

    }

    < configuration> < ! — github.com/spring-proj… –> < include resource=”org/springframework/boot/logging/logback/defaults.xml” />

    < property resource="application.properties">< /property> < property name="APP_NAME" value="toms" /> < property name="LOG_PATH" value="${user.home}/${APP_NAME}/logs" /> < property name="LOG_FILE" value="${LOG_PATH}/toms-root.log" />  < appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender"> < file>${LOG_FILE}/toms-root.log< /file> < encoder> < pattern>< ! [CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level] [traceId:%X{EAGLEEYE_TRACE_ID}] [%class:%line] - %m %n ]]> < /pattern> < charset>UTF-8< /charset> < /encoder> < rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> < fileNamePattern>${LOG_PATH}/logs_saved/toms-root.%d{yyyy-MM-dd}.%i.log< /fileNamePattern> < maxHistory>5< /maxHistory> <  maxFileSize>1GB< /maxFileSize> < totalSizeCap>20GB< /totalSizeCap> < /rollingPolicy> < /appender> < appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> < encoder> < pattern>${CONSOLE_LOG_PATTERN}< /pattern> < charset>utf8< /charset> < /encoder> < /appender> < ! - business log - > < appender name = "TOMS - BIZ - appender" class = "ch. Qos. Logback. Core. Rolling. RollingFileAppender" > < File>${LOG_PATH}/toms-biz.log< /File> < rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  < FileNamePattern>${LOG_PATH}/logs_saved/toms-biz.%d{yyyy-MM-dd}.%i.log< /FileNamePattern> < maxHistory>5< /maxHistory>  < maxFileSize>2GB< /maxFileSize> < totalSizeCap>20GB< /totalSizeCap> < /rollingPolicy> < encoder> < pattern>< ! [CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level] [traceId:%X{EAGLEEYE_TRACE_ID}] [%class:%line] - %m %n ]]> < /pattern> < charset>UTF-8< /charset> < /encoder> < /appender> < ! - the HSF log - > < appender name = "TOMS - HSF - appender" class = "ch. Qos. Logback. Core. Rolling. RollingFileAppender" > < File>${LOG_PATH}/toms-hsf.log< /File> < rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">  < FileNamePattern>${LOG_PATH}/logs_saved/toms-hsf.%d{yyyy-MM-dd}.%i.log< /FileNamePattern> < maxHistory>5< /maxHistory>  < maxFileSize>2GB< /maxFileSize> < totalSizeCap>20GB< /totalSizeCap> < /rollingPolicy> < encoder> < pattern>< ! [CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level] [traceId:%X{EAGLEEYE_TRACE_ID}] [%class:%line] - %m %n ]]> </pattern> < charset>UTF-8< /charset> < /encoder> < /appender> < ! General ERROR log - > < appender name = "TOMS - ERROR - appender" class = "ch. Qos. Logback. Core. Rolling. RollingFileAppender" > < File>${LOG_PATH}/toms-error.log< /File> < filter class="ch.qos.logback.classic.filter.LevelFilter"> < level>ERROR< /level> < onMatch>ACCEPT</onMatch> < onMismatch>DENY</onMismatch> < /filter> < rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> < FileNamePattern>${LOG_PATH}/logs_saved/toms-error.%d{yyyy-MM-dd}.%i.log< /FileNamePattern> < maxHistory>5< /maxHistory> < maxFileSize>2GB< /maxFileSize> < totalSizeCap>10GB< /totalSizeCap> < /rollingPolicy> < encoder> < pattern>< ! [CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level] [traceId:%X{EAGLEEYE_TRACE_ID}] [%class:%line] - %m %n ]]> < /pattern> < charset>UTF-8< /charset> < /encoder> < /appender> < ! - abnormal log - > < appender name = "TOMS - EXCEPTION - appender" class = "ch. Qos. Logback. Core. Rolling. RollingFileAppender" > < File>${LOG_PATH}/toms-exception.log< /File> < rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> < FileNamePattern>${LOG_PATH}/logs_saved/toms-exception.%d{yyyy-MM-dd}.log< /FileNamePattern> < maxHistory>5< /maxHistory> < /rollingPolicy> < encoder> < pattern><! [CDATA[%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%level] [traceId:%X{EAGLEEYE_TRACE_ID}] [%class:%line] - %m %n ]]> < /pattern> < charset>UTF-8< /charset> < /encoder> < /appender> < logger name="HSF" level="${logback.info.level}" additivity="false"> < appender-ref ref="TOMS-HSF-APPENDER"/> < /logger> < logger name="BIZ" level="${logback.info.level}" additivity="false"> < appender-ref ref="TOMS-BIZ-APPENDER"/> < appender-ref ref="TOMS-ERROR-APPENDER"/> < /logger> < logger name="EXCEPTION" level="${logback.info.level}" additivity="false"> < appender-ref ref="TOMS-EXCEPTION-APPENDER"/> <appender-ref ref="TOMS-ERROR-APPENDER"/> < /logger> < root level="INFO"> <  appender-ref ref="CONSOLE" /> < /root>Copy the code

    < /configuration>

  • Unit conversion utility class

    public class UnitConvertUtils {

    /** * the advance rate of meters and kilometers */ public static final double RATE_OF_METRE_AND_KILOMETRE = 1000d; public static final int INT_RATE_OF_METRE_AND_KILOMETRE = 1000; Public static final double RATE_OF_FEN_AND_YUAN = 100d; Public static final double INT_RATE_OF_CM3_AND_M3 = 1000000d; /** * meter to kilometer ** @param toConvert * @return exception returns null */ public static Double convertMetre2Kilometre(Long toConvert) {if (toConvert == null) { return null; } return toConvert / RATE_OF_METRE_AND_KILOMETRE; } /** * kilometre kilometre ** @param toConvert * @return exception returns null */ public static Long convertKilometre2Metre(Double toConvert) {if  (toConvert == null) { return null; } BigDecimal bigDecimal = BigDecimal.valueOf(toConvert); BigDecimal factorBigDecimal = BigDecimal.valueOf(RATE_OF_METRE_AND_KILOMETRE); return bigDecimal.multiply(factorBigDecimal).longValue(); } public static Long convertYuan2Fen(Double toConvert) {if (toConvert == null) { return null; } BigDecimal bigDecimal = BigDecimal.valueOf(toConvert); BigDecimal factorBigDecimal = BigDecimal.valueOf(RATE_OF_FEN_AND_YUAN); return bigDecimal.multiply(factorBigDecimal).longValue(); } public static Long convertYuan2Fen(String toConvert) {if;} public static Long convertYuan2Fen(String toConvert) {if (toConvert == null) { return null; } BigDecimal bigDecimal = BigDecimal.valueOf(ConvertUtils.convertString2Double(toConvert)); BigDecimal factorBigDecimal = BigDecimal.valueOf(RATE_OF_FEN_AND_YUAN); return bigDecimal.multiply(factorBigDecimal).longValue(); } public static String convertFen2Yuan(Long price) {if (price == null) {return  null; } return BigDecimal.valueOf(price).divide(new BigDecimal(RATE_OF_FEN_AND_YUAN)).toString(); } /** * kilometer converted to kilometer ** @param distance * @return */ public static Double meter2Kilometer(Long distance) {if (distance == null) { return null; } BigDecimal meter = BigDecimal.valueOf(distance); BigDecimal kilometer = meter.divide(new BigDecimal(INT_RATE_OF_METRE_AND_KILOMETRE)); return kilometer.doubleValue(); } @param volume @return */ public static String convertCm32M3(Long volume) {if (volume == null) { return null; } return BigDecimal.valueOf(volume).divide(new BigDecimal(INT_RATE_OF_CM3_AND_M3)).toString(); }Copy the code

    }

The original link

This article is the original content of Aliyun and shall not be reproduced without permission.