// 抽象类
public abstract class Logger {private String name;private boolean enabled;private Level minPermittedLevel;public Logger(String name, boolean enabled, Level minPermittedLevel) {this.name = name;this.enabled = enabled;this.minPermittedLevel = minPermittedLevel;}public void log(Level level, String message) {boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValueif (!loggable) return;doLog(level, message);}protected abstract void doLog(Level level, String message);
}
// 抽象类的子类:输出日志到文件
public class FileLogger extends Logger {private Writer fileWriter;public FileLogger(String name, boolean enabled,Level minPermittedLevel, String filepath) {super(name, enabled, minPermittedLevel);this.fileWriter = new FileWriter(filepath);}@Overridepublic void doLog(Level level, String mesage) {
// 格式化level和message,输出到日志文件fileWriter.write(...);}
}// 抽象类的子类: 输出日志到消息中间件(比如kafka)
public class MessageQueueLogger extends Logger {private MessageQueueClient msgQueueClient;public MessageQueueLogger(String name, boolean enabled,Level minPermittedLevel, MessageQueueClient msgQueueClient) {super(name, enabled, minPermittedLevel);this.msgQueueClient = msgQueueClient;}@Overrideprotected void doLog(Level level, String mesage) {
// 格式化level和message,输出到消息中间件msgQueueClient.send(...);}
}
接口
public interface Filter {void doFilter(RpcRequest req) throws RpcException;
}// 接口实现类:鉴权过滤器
public class AuthencationFilter implements Filter {@Overridepublic void doFilter(RpcRequest req) throws RpcException {
//...鉴权逻辑..}
}// 接口实现类:限流过滤器
public class RateLimitFilter implements Filter {@Overridepublic void doFilter(RpcRequest req) throws RpcException {
//...限流逻辑...}
}// 过滤器使用Demo
public class Application {// filters.add(new AuthencationFilter());
// filters.add(new RateLimitFilter());private List filters = new ArrayList<>();public void handleRpcRequest(RpcRequest req) {try {for (Filter filter : filters) {filter.doFilter(req);}} catch (RpcException e) {
// ...处理过滤结果...}
// ...省略其他处理逻辑...}
}
// 接口
public class VirtualWalletController {// 通过构造函数或者IOC框架注入private VirtualWalletService virtualWalletService;public BigDecimal getBalance(Long walletId) { ...} //查询余额public void debit(Long walletId, BigDecimal amount) { ...} //出账public void credit(Long walletId, BigDecimal amount) { ...} //入账public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) { .
//省略查询transaction的接口}
}public class VirtualWalletBo {//省略getter/setter/constructor方法private Long id;private Long createTime;private BigDecimal balance;
}public Enum TransactionType {DEBIT,CREDIT,TRANSFER;}public class VirtualWalletService {// 通过构造函数或者IOC框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWalletBo getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWalletBo walletBo = convert(walletEntity);return walletBo;}public BigDecimal getBalance(Long walletId) {return walletRepo.getBalance(walletId);}@Transactionalpublic void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();if (balance.compareTo(amount) < 0) {throw new NoSufficientBalanceException(...);}VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.DEBIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, balance.subtract(amount));}@Transactionalpublic void credit(Long walletId, BigDecimal amount) {VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.CREDIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();walletRepo.updateBalance(walletId, balance.add(amount));}@Transactionalpublic void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.TRANSFER);transactionEntity.setFromWalletId(fromWalletId);transactionEntity.setToWalletId(toWalletId);transactionRepo.saveTransaction(transactionEntity);debit(fromWalletId, amount);credit(toWalletId, amount);}
}
基于充血模型的 DDD 开发模式(钱包系统)
public class VirtualWallet { // Domain领域模型(充血模型)private Long id;private Long createTime = System.currentTimeMillis();private BigDecimal balance = BigDecimal.ZERO;public VirtualWallet(Long preAllocatedId) {this.id = preAllocatedId;}public BigDecimal balance() {return this.balance;}public void debit(BigDecimal amount) {if (this.balance.compareTo(amount) < 0) {throw new InsufficientBalanceException(...);}this.balance = this.balance.subtract(amount);}public void credit(BigDecimal amount) {if (amount.compareTo(BigDecimal.ZERO) < 0) {throw new InvalidAmountException(...);}this.balance = this.balance.add(amount);}
}public class VirtualWalletService {// 通过构造函数或者IOC框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWallet getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);return wallet;}public BigDecimal getBalance(Long walletId) {return walletRepo.getBalance(walletId);}@Transactionalpublic void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.debit(amount);VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.DEBIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, wallet.balance());}@Transactionalpublic void credit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.credit(amount);VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.CREDIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, wallet.balance());}@Transactionalpublic void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {
//...跟基于贫血模型的传统开发模式的代码一样...}
}
基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比,主要区别在 Service
层。在基于充血模型的开发模式下,我们将部分原来在 Service 类中的业务逻辑移动到了一个
充血的 Domain 领域模型中,让 Service 类的实现依赖这个 Domain 类。
在基于充血模型的 DDD 开发模式下,Service 类并不会完全移除,而是负责一些不适合放在
Domain 类中的功能。比如,负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂
等事务等非功能性的工作。
进一步变化为:
1、2、6、7 都是跟 token 有关,负责 token 的生成、验证;(AuthToken)
3、4 都是在处理 URL,负责 URL 的拼接、解析;(Url)
5 是操作 AppID 和密码,负责从存储中读取 AppID 和密码。(CredentialStorage)
public interface ApiAuthenticator {void auth(String url);void auth(ApiRequest apiRequest);
}
public class DefaultApiAuthenticatorImpl implements ApiAuthenticator {private CredentialStorage credentialStorage;public DefaultApiAuthenticatorImpl() {this.credentialStorage = new MysqlCredentialStorage();}public DefaultApiAuthenticatorImpl(CredentialStorage credentialStorage) {this.credentialStorage = credentialStorage;}@Overridepublic void auth(String url) {ApiRequest apiRequest = ApiRequest.buildFromUrl(url);auth(apiRequest);}@Overridepublic void auth(ApiRequest apiRequest) {String appId = apiRequest.getAppId();String token = apiRequest.getToken();long timestamp = apiRequest.getTimestamp();String originalUrl = apiRequest.getOriginalUrl();AuthToken clientAuthToken = new AuthToken(token, timestamp);if (clientAuthToken.isExpired()) {throw new RuntimeException("Token is expired.");}String password = credentialStorage.getPasswordByAppId(appId);AuthToken serverAuthToken = AuthToken.generate(originalUrl, appId, password,if (!serverAuthToken.match(clientAuthToken)) {throw new RuntimeException("Token verfication failed.");}}
}
// API 接口监控告警 这种情况进行扩展就需要对方法进行修改
public class Alert {private AlertRule rule;private Notification notification;public Alert(AlertRule rule, Notification notification) {this.rule = rule;this.notification = notification;}public void check(String api, long requestCount, long errorCount, long duration) {long tps = requestCount / durationOfSeconds;if (tps > rule.getMatchedRule(api).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {notification.notify(NotificationEmergencyLevel.SEVERE, "...");}}
}
// 当每秒钟接口超时请求个数,超过某个预先设置的最大阈值时,我们也要触发告警发送通知。
代码改动
public class Alert {// ...省略AlertRule/Notification属性和构造函数...
// 改动一:添加参数timeoutCountpublic void check(String api, long requestCount, long errorCount, long timeoutCount) {long tps = requestCount / durationOfSeconds;if (tps > rule.getMatchedRule(api).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {notification.notify(NotificationEmergencyLevel.SEVERE, "...");}// 改动二:添加接口超时处理逻辑long timeoutTps = timeoutCount / durationOfSeconds;if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}}
}
重构–》
public class Alert {private List alertHandlers = new ArrayList<>();public void addAlertHandler(AlertHandler alertHandler) {this.alertHandlers.add(alertHandler);}public void check(ApiStatInfo apiStatInfo) {for (AlertHandler handler : alertHandlers) {handler.check(apiStatInfo);}}
}public class ApiStatInfo {//省略constructor/getter/setter方法private String api;private long requestCount;private long errorCount;private long durationOfSeconds;
}public abstract class AlertHandler {protected AlertRule rule;protected Notification notification;public AlertHandler(AlertRule rule, Notification notification) {this.rule = rule;this.notification = notification;}public abstract void check(ApiStatInfo apiStatInfo);
}public class TpsAlertHandler extends AlertHandler {public TpsAlertHandler(AlertRule rule, Notification notification) {super(rule, notification);}@Overridepublic void check(ApiStatInfo apiStatInfo) {long tps = apiStatInfo.getRequestCount() / apiStatInfo.getDurationOfSeconds();if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}}
}public class ErrorAlertHandler extends AlertHandler {public ErrorAlertHandler(AlertRule rule, Notification notification) {super(rule, notification);}@Overridepublic void check(ApiStatInfo apiStatInfo) {if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).gnotification.notify(NotificationEmergencyLevel.SEVERE, "...");}
}public class ApplicationContext {private AlertRule alertRule;private Notification notification;private Alert alert;public void initializeBeans() {alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码notification = new Notification(/*.省略参数.*/); //省略一些初始化代码alert = new Alert();alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));}public Alert getAlert() {return alert;}// 饿汉式单例private static final ApplicationContext instance = new ApplicationContext();private ApplicationContext() {initializeBeans();}public static ApplicationContext getInstance() {return instance;}
}public class Demo {public static void main(String[] args) {ApiStatInfo apiStatInfo = new ApiStatInfo();
// ...省略设置apiStatInfo数据值的代码ApplicationContext.getInstance().getAlert().check(apiStatInfo);}
}
针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思
路。
积分系统设计案例
综合考虑,我们更倾向于第一种和第二种模块划分方式。但是,不管选择这两种中的哪一种,积分系统所负责的工作是一样的,只包含积分的增、减、查询,以及积分明细的记录和查询。
设计模块与模块之间的交互关系
上下层系统之间的调用倾向于通过同步接口,同层之间的调用倾向于异步消息调用。比如,营销系统和积分系统是上下层关系,它们之间就比较推荐使用同步接口调用(上层是调用系统,下层是被调用系统)
设计模块的接口、数据库、业务模型
积分流水明细表
表设计这里还可以加一个积分余额表(没有积分余额表容易造成多扣)
积分系统的接口
编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具
体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。
重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编
码规范这些理论。
当我们面对函数抛出异常的时候,应该选择上面的哪种处理方式呢?
下一篇:4. 操作列表