Plug n Play REST API Development With Simple-Orchestrator
Simple-Orchestrator is a Java lightweight workflow orchestration framework, it helps to modulate, isolate, and reuse your application's functional tasks.
Join the DZone community and get the full member experience.
Join For Free1. Sample Orchestrator Execution Flow:
Code for the Above Diagram:
public interface CreditScoreContext extends HistoryContext<String> {
String getSsn();
void setCreditScore(int score);
}
public interface LoanContext extends HistoryContext<String> {
LoanApplication getApplication();
int getCreditScore();
int getRiskScore();
void setLoanApproveStatus(boolean isApproved);
}
@Component
public class CreditScoreTask implements Task<CreditScoreContext> {
@Override
public void execute(CreditScoreContext context) throws OrchestratorException {
context.logHistory(this.getClass().getName());
String ssn = context.getSsn();
if(ssn != null && ssn.length()>0){
context.setCreditScore((ssn.charAt(0)-'0')*100);
} else
context.setCreditScore(0);
}
}
@Component
public class LoanTask implements Task<LoanContext> {
@Override
public void execute(LoanContext context) throws OrchestratorException {
context.logHistory(this.getClass().getName());
LoanApplication app = context.getApplication();
int creditScore = context.getCreditScore();
if(creditScore>650 && app.getSalary()>0 && app.getLoanAmount()/app.getSalary()<5)
context.setLoanApproveStatus(true);
else
context.setLoanApproveStatus(false);
}
}
The Service: (Create and Run Orchestrator)
@Component
@Path("/application/loan")
public class LoanService {
@Autowired
ValidationTask<LoanApplication> validationTask;
@Autowired
CreditScoreTask creditScoreTask;
@Autowired
AsyncCheckRiskTask asyncCheckRiskTask;
@Autowired
LoanTask loanTask;
@Autowired
Orchestrator<LoanOrchestratorConext> orchestrator;
@Bean
ValidationTask<LoanApplication> loanValidationTask(){
return new ValidationTask<LoanApplication>();
}
@Bean
Orchestrator<LoanOrchestratorConext> loanOrchestrator(){
return new OrchestratorImpl<LoanOrchestratorConext>(
validationTask,creditScoreTask, asyncCheckRiskTask, loanTask);
}
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public AppliationResult applyloan(LoanApplication app) {
LoanOrchestratorConext context = new LoanOrchestratorConext() ;
context.init(app);
try {
orchestrator.execute(context);
} catch (ValidationException e){
return new AppliationResult(e.getErrors());
}
return new AppliationResult(context.getApplication().getPerson().getName(), context.isApproved(), context.getHistory());
}
}
2. Plug and Play
Because a task depends on its context interface only, a task can be plugged into any orchestrator as long as the orchestrator's context implements the task's context interface.
The CreditScoreTask can be added into CardService (as in LoanService):
@Component
@Path("/application/card")
public class CardService {
@Autowired
ValidationTask<CardApplication> validationTask;
@Autowired
CreditScoreTask creditScoreTask;
@Autowired
DebitCardTask debitCardTask;
@Autowired
CreditCardTask creditCardTask;
@Autowired
AsyncCheckRiskTask asyncCheckRiskTask;
@Autowired
Channel<RiskCreditContext> riskCreditChannel;
@Autowired
Selector<CardApplication.CardType, CardSelectorConext> cardSelector;
@Autowired
Orchestrator<CardOrchestratorConext> orchestrator;
@Bean
ValidationTask<CardApplication> cardValidationTask(){
return new ValidationTask<CardApplication>();
}
@Bean
Channel<RiskCreditContext> riskCreditChannel(){
return new Channel<RiskCreditContext>(asyncCheckRiskTask, creditCardTask);
}
@Bean
Selector<CardApplication.CardType, CardSelectorConext> cardSelector(){
return new CardSelector(new EnumMap(CardApplication.CardType.class){{
put(CardApplication.CardType.Credit, riskCreditChannel);
put(CardApplication.CardType.Debit, debitCardTask);
}});
}
@Bean
Orchestrator<CardOrchestratorConext> cardOrchestrator(){
return new OrchestratorImpl<CardOrchestratorConext>(creditScoreTask, cardSelector);
}
@PostConstruct
public void init(){
System.out.println("aaa");
}
static private interface RiskCreditContext extends AsyncCheckRiskContext, CreditCardContext {};
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public AppliationResult applyCard(CardApplication app) {
CardOrchestratorConext context = new CardOrchestratorConext() ;
context.init(app);
orchestrator.execute(context);
return new AppliationResult(context.getApplication().getPerson().getName(), context.isApproved(), context.getHistory());
}
}
3. Orchestration with Selector and Channel:
A Few Other Tips and Tricks
Now that we've seen how to code our orchestrator, and what this code will result in, here are a few more tips and tricks to help you get the most out of this tool:
4. Type Safe
Since Orchestrator, Context, Task, Channel, and Selector are typed, a mismatch will result in a compile time error.
5. Minimum Coding
You should code in business logic, not boilerplate code. The framework will prepare data for the next task and process the task result for you.
6. Isolation
A task can only see it's own context interface, it has no access to other properties of the object upon which it executes.
7. Asynchronous Tasks
Easy implementation for asynchronous tasks with natural dependency order.
8. Integration
With Tasks calling other services, the orchestrator can be used for integration service.
9. Open Sourced
For complete framework project and sample project, see my GitHub page.
Opinions expressed by DZone contributors are their own.
Comments