生活随笔
收集整理的這篇文章主要介紹了
Java应用结构规范
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡介:在Java程序開發中,命名和應用分層無疑是廣大后端同胞的兩大“痛點”,本文提供一種基于領域模型的輕量級應用分層結構設計,供大家參考。下面按分層結構、分層明細、調用關系、各層規范和通用代碼工具展開介紹。
作者 | 阿卓
來源 | 阿里技術公眾號
序言
在Java程序開發中,命名和應用分層無疑是廣大后端同胞的兩大“痛點”,本文提供一種基于領域模型的輕量級應用分層結構設計,供大家參考。下面按分層結構、分層明細、調用關系、各層規范和通用代碼工具展開介紹。
一 分層結構
通過調用業務層服務,處理前端的請求。
提供封裝好的能力,并通過對能力進行組裝、編排,進行業務邏輯處理。
對底層數據源進行增刪改查操作。
定義暴露給其他應用的接口。
定義暴露給外部的公共類。
通過調用業務層服務,處理外部應用的請求。
二 分層明細
web(前端請求層)
biz(業務層)
dal(數據層)
client(外部請求層)
common(外部公共層)
facade(外觀層)
start(啟動類)
qatest(測試類)
三 調用關系
注意點:
- 服務和服務直接可以互相調用;
- 服務可以調用多個域的域能力;
- 域能力是封裝好的最小顆粒度的能力,不可互相調用;
- 查詢服務直接調用manager,不調用域能力;
四 各層規范
web(前端請求層)
- 定義統一的異常處理切面:處理業務異常和其他運行時異常;
biz(業務層)
- 內部服務不做異常處理和返回result封裝類,異常都拋給web層和facade層處理。
- 查詢服務和其他服務區分開,單獨放在一個包中;
- 能力唯一對應一個域,且是封裝好的最小顆粒度的能力。
- 外部服務要在remote中做好異常處理和封裝;
- 業務層中的common類為僅在應用內部使用的公共類;
dal(數據層)
- mapper要按不同類型的數據源分開存放,如adb和xdb。
common(外部公共層)
- common只存放暴露給外部的實體類、常量和枚舉;
- 暴露給外部的dto只保留外部必要的字段,其他字段如feature等不可存在。
facade(外觀層)
- 定義統一的異常處理切面:處理業務異常和其他運行時異常;
- facade層的hsf實現類只做簡單的參數校驗和轉化,不要寫業務邏輯。
五 通用代碼和工具
web(前端請求層)
@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());}
}
biz(業務層)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public interface AllLoggers {/*** 應用日志*/Logger APPLICATION = LoggerFactory.getLogger("APPLICATION");/*** 異常日志*/Logger EXCEPTION = LoggerFactory.getLogger("EXCEPTION");/*** 業務日志*/Logger BIZ = LoggerFactory.getLogger("BIZ");/*** hsf日志*/Logger HSF = LoggerFactory.getLogger("HSF");/*** 入口日志*/Logger MTOP = LoggerFactory.getLogger("MTOP");}
< ?xml version="1.0" encoding="UTF-8"?>
< configuration>< !-- https://github.com/spring-projects/spring-boot/blob/v1.5.13.RELEASE/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml -->< 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>< !--業務日志-->< 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>< !--hsf日志-->< 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>< !-- 通用錯誤日志 -->< 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>< !-- 異常日志 -->< 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>
< /configuration>
public class UnitConvertUtils {/*** 米和千米的進率*/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;/*** 米轉千米** @param toConvert* @return 異常返回null*/public static Double convertMetre2Kilometre(Long toConvert) {if (toConvert == null) {return null;}return toConvert / RATE_OF_METRE_AND_KILOMETRE;}/*** 千米轉米** @param toConvert* @return 異常返回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();}/*** 元轉分** @param toConvert* @return 異常返回null*/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();}/*** 元轉分** @param toConvert* @return 異常返回null*/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();}/*** 分轉元** @param price* @return*/public static String convertFen2Yuan(Long price) {if (price == null) {return null;}return BigDecimal.valueOf(price).divide(new BigDecimal(RATE_OF_FEN_AND_YUAN)).toString();}/*** 里程米轉換為千米** @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();}}
原文鏈接
本文為阿里云原創內容,未經允許不得轉載。?
總結
以上是生活随笔為你收集整理的Java应用结构规范的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。