在我的DDD项目中,我正在尝试使用java enum实现状态模式。
在验证具有取决于状态的行为的实体方法时遇到问题。
为验证我使用通知模式。
我遵循“始终有效的实体”方法,因此在每次操作中我首先调用“isValidForOperation”验证方法。
这是代码,只是为了简单起见:
实体:
public class Task extends AggregateRoot<TaskId> {
...
private State state;
...
// Operation with behaviour depending on the state
// It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
// I apply the state pattern here
public void start () {
State next = this.state.start ( this );
this.setState ( next );
}
...
}
对状态建模的java枚举:
public enum State {
ASSIGNED {
public State start ( Task task ) {
// Validation method to ensure the operation can be done
assertTaskIsValidForStart ( task );
// Business logic
...
// Return the next state
return ( State.IN_PROGRESS );
}
}
...
// more enum values for other states
...
// Default implementation of "start" operation
// It will be executed when the current state is not "ASSIGNED"
// So an error would be generated
public State start ( Task task ) {
// I can't apply notification pattern here !!!
// I would have to throw an exception
}
}
验证方法遵循通知模式。 它收集通知对象中的所有可能错误。 此通知对象将传递给异常。 抛出异常,然后应用程序层捕获它并将所有错误消息返回给客户端。
public void assertTaskIsValidForStart ( Task task ) {
Notification notification = new Notification();
if ( errorCondition (task) ) {
notification.addError(...);
}
...
// more errors
...
if ( notification.hasErrors() ) {
throw new TaskNotValidForStartException ( notification.errors() );
}
}
当错误条件是关于状态之间的无效转换时,如何应用通知模式(与状态模式结合)?
有什么想法吗?
更新
我找到了解决方案。我将整个操作依赖于实体中的状态,并将状态模式应用于所需的代码。这样我应用模式来计算下一个状态,这样我就可以检查是否允许转换并应用通知模式。
代码:
public class Task extends AggregateRoot<TaskId> {
...
private State state;
...
// Operation with behaviour depending on the state
// It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
// I apply fine-grained state pattern here
public void start () {
// Validation method to ensure the operation can be done
// One of the validations will be if the transition is allowed
assertTaskIsValidForStart ( this );
// Business logic
// If it depends on the state, I would apply state pattern delegating to another method
...
// Set the next state
State next = this.nextStateForStart();
this.setState ( next );
}
...
public State currentState() {
return this.state;
}
...
public State nextStateForStart() {
return this.currentState().nextStateForStart();
}
...
}
public enum State {
ASSIGNED {
public State nextStateForstart() {
return ( State.IN_PROGRESS );
}
}
...
// more enum values for other states
...
// Default implementation of "start" transition
// It will be executed when the current state is not "ASSIGNED"
public State nextStateForstart() {
return null;
}
}
public void assertTaskIsValidForStart ( Task task ) {
Notification notification = new Notification();
// Validate the transition is allowed
if ( task.nextStateForStart() == null ) {
notification.addError(...);
}
...
// more errors
...
if ( notification.hasErrors() ) {
throw new TaskNotValidForStartException ( notification.errors() );
}
}
答案 0 :(得分:1)
我觉得你的枚举太过分了。 除了具有几乎无法扩展的固定状态集之外,您还很难为每个具体状态引入任何形式的合同,这也将解决您的通知问题。
引入一个抽象状态类,它是所有具体状态的基类。传递上下文,允许为每个状态设置后继状态。此上下文可以由您的聚合根实现。 您可以通过使用AbstracftState强制执行的方式管理您的通知,例如,通过强制状态执行返回通知对象:
interface StateContext {
setState(AbstractState state);
}
class AbstractState {
abstract Notification execute(StateContext context);
}
class Task extends AggregateRoot implements StateContext {
AbstractState currentState;
....
public void start() {
Notification n = currentState.execute(this);
if (n.hasErrors()) {
throw new Exception(n.toErrorReport());
}
}
}
现在,您可以在执行之前或之后收集每个状态的错误(您可能希望在执行前调用的每个AbstractState中引入validateStart())并将收集的错误报告给调用者。
答案 1 :(得分:0)
我会将TaskWorkflow建模为Task聚合中的VO。
class Task {
private Workflow workflow;
public void start() {
workflow = workflow.followWith(Action.START, this);
}
public State currentState() {
return workflow.state();
}
public List availableActions() {
return workflow.nextActions();
}
}
工作流程是由操作连接的状态之间的转换组成的FSM。对工作流方法的任何调用都会创建一个指向新状态的新工作流表示。转换可以建模为直接或更复杂的自定义涉及业务逻辑,就像你说的那样。 如果您使用函数式语言,则可以返回Monad来处理错误,但在这种情况下,您可以重新创建并创建一个,或者您可以抛出表示聚合消息的异常。
希望它有所帮助。