In this guide, we'll deploy a rules based compliance engine for financial trades, where rules require data sourced from different locations, and in specific formats.
We'll assume you've covered the previous walkthroughs, so we'll skip the basics and get straight to the main course.
This walkthrough doesn't have the full code for the three services - we just highlight the parts that are interesting. The full source for this demo is available on Gitlab, here.
Vyne uses Taxi to allow services to describe constraints they have on input parameters.
These are the classes & services we'll be using for this demo:
@Serviceclass TradeValueRuleService {@Operation@PostMapping("/rules/traderLimits")fun evaluate(@RequestBody request: TradeValueRuleRequest):TradeValueRuleResponse {// Not shown}}@DataTypetypealias TradeValue = Money@DataType@ParameterTypedata class TradeValueRuleRequest(@Constraint("currency = 'USD'")val tradeValue: TradeValue,val traderLimit: TraderMaxTradeValue)@DataType("demo.Money")@ParameterTypedata class Money(val currency: Currency,val value: MoneyAmount)
@Servicepublic class TradeValueRuleService {@Operation@PostMapping("/rules/traderLimits")TradeValueRuleResponse evalute(@RequestBody TradeValueRuleRequest request) {// Not shown}}​@DataType("demo.TradeValueRuleRequest")@ParameterTypepublic class TradeValueRuleRequest {@Constraint("currency = 'USD'")private final Money tradeValue;private final TraderMaxTradeValue traderLimit;​public TradeValueRuleRequest(Money tradeValue, TraderMaxTradeValue traderLimit) {this.tradeValue = tradeValue;this.traderLimit = traderLimit;}}@DataType("demo.Money")@ParameterTypepublic class Money {@DataType("demo.Currency")private final Currency currency;@DataType("demo.Value")private final MoneyAmount value;​public Money(Currency currency, MoneyAmount value) {this.currency = currency;this.value = value;}}
This service evaluates a rule to do with TradeValue. The actual implementation of the rule isn't as interesting as the contract itself - let's take a look in closer detail:
@Operationfun evaluate(@RequestBody request: TradeValueRuleRequest):TradeValueRuleResponse {}​data class TradeValueRuleRequest(@Constraint("currency = 'USD'")val tradeValue: TradeValue,val traderLimit: TraderMaxTradeValue)
@OperationTradeValueRuleResponse evalute(@RequestBody TradeValueRuleRequest request) {}​@DataType("demo.TradeValueRuleRequest")@ParameterTypepublic class TradeValueRuleRequest {@Constraint("currency = 'USD'")private final Money tradeValue;private final TraderMaxTradeValue traderLimit;​public TradeValueRuleRequest(Money tradeValue, TraderMaxTradeValue traderLimit) {this.tradeValue = tradeValue;this.traderLimit = traderLimit;}}
Our Operation
takes a TradeValueRequest
as it's parameter - which in turn, declares a constraint on the tradeValue
parameter - the currency of the tradeValue must be expressed in USD.
Let's run a couple of queries, and see how Vyne applies this constraint.
We'll use the TradeRequest
as a starting point for our query, and populate it as follows:
When we submit this query, you should see the service gets invoked, and we get our response back. This is possible, because the currency we provided of USD
satisfies the constraint.
If we change the Currency to USD
and re-run the same query, we should get a different response:
We need something that can convert currencies!
Contracts on Operations describe what functionality an operation provides. It's how Vyne can discover services that resolve constraints.
Over in another service, we have a RateConverterService, which has the following operation:
@PostMapping("/tradeValues/{targetCurrency}")@ResponseContract(basedOn = "source", constraints = ResponseConstraint("currency = targetCurrency"))@Operationfun convert(@PathVariable("targetCurrency") targetCurrency: Currency, @RequestBody source: TradeValue): TradeValue {
@PostMapping("/tradeValues/{targetCurrency}")@ResponseContract(basedOn = "source", constraints = new ResponseConstraint("currency = targetCurrency"))@OperationTradeValue convert(@PathVariable("targetCurrency") Currency targetCurrency, @RequestBody TradeValue source) {}
The @ResponseContract
on this operation tells use that it returns a TradeValue
, that is based on the source
parameter, but with it's currency
value updated to the targetCurrency
provided in the request.
It's perhaps a little easier to read in the generated Taxi schema:
operation convert( source : Money( currency = "GBP" ),targetCurrency : String ) : Money( from source, currency = targetCurrency )
In other words - it converts currencies. Perfect!
With this service deployed, if we re-run the same query, we now get a different response & execution plan:
Note that now that a service is exposed that can convert currencies, Vyne automatically invokes it and then uses the returned result to call our TradeValueRuleService
.
This walkthrough has shown how Vyne understands constraints on operation inputs, and when a constraint isn't satisfied, how Vyne will attempt to leverage existing services to automatically adapt data.
​