I am implementing a payment system in my capsule. But getting error for using the currency in SPS payment method in endpoints.
payment {
amount (50)
currency (payInfo.currency)
item {
name ("abcd")
amount (50)
count (1)
}
tax {
amount (0)
}
}
Currently, we have used static amount, item details, etc.
Structure for Currency :
enum (Currency) {
symbol (USD)
symbol (EUR)
symbol (VUV)
}
PayInfo Structure:
structure (payInfo) {
property (currency) {
type (Currency)
min (Optional)
max (One)
}
}
Bixby Reference which I used :
https://bixbydevelopers.com/dev/docs/dev-guide/developers/actions.payments
https://bixbydevelopers.com/dev/docs/reference/type/endpoints.action-endpoints.action-endpoint.payment-methods.sps.payment.currency
----------------------------Issue 2----------------------------------------
We made a structure Information as
structure (Information){
property (amount) {
type (Amount)
min (Optional)
max (Many)
}
property (currency) {
type (Currency)
min (Optional)
max (Many)
}
}
And Structure Currency as
enum (Currency) {
symbol (USD)
symbol (EUR)
symbol (VUV)
}
After that In Action PaymentAction, we defined structure Information as a type for input payment.
action (PaymentAction) {
type(Commit)
collect {
input (payInfo) {
type (Information)
min (Optional)
}
}
output (makepayment)
}
And added one utterance in training as :
[g:PaymentAction] Make Payment
Through this our error gets resolved, But when we run code in simulator, getting Capsule Execution Error as Com.google.common.base.VerifyException: '' is not a valid ISO 4217 currency code.
I think the error message means payInfo is not found. It should be a concept as an input, not the structure name. Also I would recommend rename the structure to PayInfo instead of payInfo.
Please see the attached screenshot.
Related
For an attribute which need to be validated, lets say for an entity we have country field as VO
This country field needs to be validated to be alpha-3 code as per some business logic required by domain expert.
NOTE:
*We need to persist this country data as it can have other values also and possible in future there can be addition, updating and deleting of the country persisted data.
This is just one example using country code which may rarely change, there can be other fields which needs to be validated from persistence like validating some quantity with wrt data in persistence and it won't be efficient to store them in memory or prefetching them all.
Another valid example can be user creation with unique and valid domain email check, which will need uniqueness check from persistence
*
Case 1.
Doing validation in application layer:
If we call repository countryRepo.getCountryByCountryAlpha3Code() in application layer and then if the value is correct and valid part of system we can then pass the createValidEntity() and if not then can throw the error directly in application layer use-case.
Issue:
This validation will be repeated in multiple use-case if same validation need to be checked in other use-cases if its application layer concern
Here the business logic is now a part of application service layer
Case 2
Validating the country code in its value object class or domain service in Domain Layer
Doing this will keep business logic inside domain layer and also won't violate DRY principle.
import { ValueObject } from '#shared/core/domain/ValueObject';
import { Result } from '#shared/core/Result';
import { Utils } from '#shared/utils/Utils';
interface CountryAlpha3CodeProps {
value: string;
}
export class CountryAlpha3Code extends ValueObject<CountryAlpha3CodeProps> {
// Case Insensitive String. Only printable ASCII allowed. (Non-printable characters like: Carriage returns, Tabs, Line breaks, etc are not allowed)
get value(): string {
return this.props.value;
}
private constructor(props: CountryAlpha3CodeProps) {
super(props);
}
public static create(value: string): Result<CountryAlpha3Code> {
return Result.ok<CountryAlpha3Code>(new CountryAlpha3Code({ value: value }));
}
}
Is it good to call the repository from inside domain layer (Service
or VO (not recommended) ) then dependency flow will change?
If we trigger event how to make it synchronous?
What are some better ways to solve this?
export default class UseCaseClass implements IUseCaseInterface {
constructor(private readonly _repo: IRepo, private readonly countryCodeRepo: ICountryCodeRepo) {}
async execute(request: dto): Promise<dtoResponse> {
const someOtherKeyorError = KeyEntity.create(request.someOtherDtoKey);
const countryOrError = CountryAlpha3Code.create(request.country);
const dtoResult = Result.combine([
someOtherKeyorError, countryOrError
]);
if (dtoResult.isFailure) {
return left(Result.fail<void>(dtoResult.error)) as dtoResponse;
}
try {
// -> Here we are just calling the repo
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
const dataOrError = MyEntity.create({...request,
key: someOtherKeyorError.city.getValue(),
country: countryOrError.getValue(),
});
const commandResult = await this._repo.save(dataOrError.getValue());
return right(Result.ok<any>(commandResult));
} catch (err: any) {
return left(new AppError.UnexpectedError(err)) as dtoResponse;
}
}
}
In above application layer,
this part of code :
const isValidCountryCode = await this.countryCodeRepo.getCountryCodeByAlpha2Code(countryOrError.getValue()); // return boolean value
if (!isValidCountryCode) {
return left(new ValidCountryCodeError.CountryCodeNotValid(countryOrError.getValue())) as dtoResponse;
}
it it right to call the countryCodeRepo and fetch result or this part should be moved to domain service and then check the validity of the countryCode VO?
UPDATE:
After exploring I found this article by Vladimir Khorikov which seems close to what I was looking, he is following
As per his thoughts some domain logic leakage is fine, but I feel it will still keep the value object validation in invalid state if some other use case call without knowing that persistence check is necessary for that particular VO/entity creation.
I am still confused for the right approach
In my opinion, the conversion from String to ValueObject does not belong to the Business Logic at all. The Business Logic has a public contract that is invoked from the outside (API layer or presentation layer maybe). The contract should already expect Value Objects, not raw strings. Therefore, whoever is calling the business logic has to figure out how to obtain those Value Objects.
Regarding the implementation of the Country Code value object, I would question if it is really necessary to load the country codes from the database. The list of country codes very rarely changes. The way I've solved this in the past is simply hardcoding the list of country codes inside the value object itself.
Sample code in pseudo-C#, but you should get the point:
public class CountryCode : ValueObject
{
// Static definitions to be used in code like:
// var myCountry = CountryCode.France;
public static readonly CountryCode France = new CountryCode("FRA");
public static readonly CountryCode China = new CountryCode("CHN");
[...]
public static AllCountries = new [] {
France, China, ...
}
public string ThreeLetterCode { get; }
private CountryCode(string threeLetterCountryCode)
{
ThreeLetterCode = threeLetterCountryCode;
}
public static CountryCode Parse(string code)
{
[...] handle nulls, empties, etc
var exists = AllCountries.FirstOrDefault(c=>c.ThreeLetterCode==code);
if(exists == null)
// throw error
return exists;
}
}
Following this approach, you can make a very useful and developer-friendly CountryCode value object. In my actual solution, I had both the 2 and 3-letter codes and display names in English only for logging purposes (for presentation purposes, the presentation layer can look up the translation based on the code).
If loading the country codes from the DB is valuable for your scenario, it's still very likely that the list changes very rarely, so you could for example load a static list in the value object itself at application start up and then refresh it periodically if the application runs for very long.
In the below code, although function test2 in invalid, it should not affect to testing of function test1:
export function test1(): boolean {
return true;
}
export function test2(): number {
return "1";
}
Test:
import { assert as Assert } from "chai";
import { test1 } from "./example";
describe("Example", (): void => {
it("Example", (): void => {
Assert.isTrue(test1());
});
});
However Mocha, executed by TSNode will fail:
TSError: ⨯ Unable to compile TypeScript:
Source/ProjectBuilder/example.ts(6,3): error TS2322: Type '"1"' is not assignable to type 'number'.
Possible to force TSNode to complete above test? TypeScript has option noEmitOnError...
My mocharc.yaml:
extension:
- ts
spec: "**/*.test.ts"
require:
- ts-node/register
- tsconfig-paths/register
Why I need it
According the Clean Architecture, "Business rules must not depends on anything, and it must be testable". But even if business rules does not depend on views, controllers, etc., we can not test it if some errors in views, controllers, etc. exists.
When I modify business logic in my application, all that depend on it becomes invalid. Assume that in my entity in business logic type User { name: string; age: number } has been changed to type User { fullName: string; age: string; }. All that depend on business logic (view, controllers, presenters, etc.) becomes invalid - I need to correct it too according updated to business logic.
// In business rules:
type User = {
ID: number;
fullName: string;
age: string; // in past - number
}
// In controller:
class UserProfileController {
// ...
// All what comes from input field is string ↓
private onClickChangeUserAge(inputtedUserAge: string) {
// This expression obeys to previous version of business rules!
this.targetUser.age = Number(inputtedUserAge);
// TypeScript will emit type error, when I try to test business rules,
// however business rules does not depend on controller.
}
}
However, in big application, this corrections could takes days, weeks and months. How we can get the feedback proves that at least business logic works properly and we are in right way?
It would not make sense for you to ask Typescript to disregard types. What you are describing is akin to asking for a calculator that confirms that 2 + 2 = 5, only temporarily, because eventually you will ask for 2 + 3 = 5 but that might take a very long time.
The assumption in your question is that number and string are sufficiently compatible that your business logic will be sound regardless of whether test2 returns 1 or "1". This is not true: By way of example, 1 + 1 == 2, "1" + 1 == "11", and "1" - 1 == 0. Depending on which of those applies to your case, disregarding types may allow you to test your business logic, or may give you false confidence in a broken system. I would venture that in general the data types count as part of the business logic you're trying to verify--what would the data model count as, if not business logic?
Instead, what you are describing is a gradual migration during which the business rules ensure that test2 returns string | number. By describing the migration accurately in the type system, you may be able to confirm that the business logic applies correctly--if that is true across your migration--and also provide a type that you can apply only from certain modules and call sites that can be tightened up as you finish the migration.
type User = {
ID: number;
fullName: string;
age: string | number
}
You can specify both types to make migration easier and not use //#ts-ignore everywhere.
This will add proper suggestions everywhere like:
After reading DDD - Modifications of child objects within aggregate and Update an entity inside an aggregate I still puzzled with the implementation of entity changes within an aggregate. For what I understand the aggregate root speaks for the whole (or entire aggregate) and delegates 'commands' changes down to the rest.
This last part, the delegating down to the rest is causing some problems. In the example below I want to change the quantity of a particular orderline. I'm addressing the root 'Order' and telling it to change the quantity of a orderline identified by a local identifier.
When all business rules are met an event can be created and applied on the aggregate. For now all the events are applied on the aggregate root, and I think that is a good practices, so all the commands are directed on the root and this changes the state of the aggregate. Also the aggregate root is the only one creating events, letting the world know what happened.
class Order extends AggregateRoot
{
private $orderLines = [];
public function changeOrderLineQuantity(string $id, int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->applyChange(new OrderLineQuantityChangedEvent(
$id, $quantity
));
}
private function onOrderLineQuantityChangedEvent(OrderLineQuantityChangedEvent $event)
{
$orderLine = $this->orderLines[$event->getId()];
$orderLine->changeQuantity($event->getQuantity());
}
}
class OrderLine extends Entity
{
private $quantity = 0;
public function changeQuantity(int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->quantity = $quantity;
}
}
But, when I am applying this implementation I have a problem, as you notice the business rule for checking the value of $quantity is located in two classes. This is on purpose, because I don't really know the best spot. The rule is only applied within the OrderLine class, thus it doesn't belong in Order. But when I'm removing this from Order events will be created that cannot be applied, because not all business rules are met. This is also something that is not wanted.
I can created a method in the class OrderLine like:
public function canChangeQuantity(int $quantity)
{
if ($quantity < 0) {
return false;
}
return true;
}
changing the method in the OrderLine to:
public function changeQuantity(int $quantity)
{
if ($this->canChangeQuantity($quantity) < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->quantity = $quantity;
}
Now I can alter the method within the Order class to:
public function changeOrderLineQuantity(string $id, int $quantity)
{
$orderLine = $this->orderLines[$event->getId()];
if ($orderLine->canChangeQuantity($quantity)) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this->applyChange(new OrderLineQuantityChangedEvent(
$id, $quantity
));
}
Ensuring the business logic is where it belongs and also not in two places. This is an option, but if the complexity increases and the model becomes larger I can imagine that these practices become more complex.
For now I have to questions:
(1) How do you cope with alterations deep within the aggregate that are started from the root?
(2) When the business rules increase (e.g, max quantity is 10, but on Monday 3 more, and for product X max is 3 items). Is it good practices to supply each command / method on the aggregate root a domain services that is validating these business rules?
I have a problem, as you notice the business rule for checking the value of $quantity is located in two classes.
From an "object oriented" perspective, Order::changeOrderLineQuantity($id, $quantity) is a message. It is normal for messages to have schema, and for schema to restrict the range of values that are permitted in any given field.
So this code here:
public function changeOrderLineQuantity(string $id, int $quantity)
{
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
is an example of message validation, you are checking to see that quantity is in the allowed range of values because the general-purpose int primitive is too permissive.
What domain modelers using strongly typed languages will often do here is introduce a new type, aka a ValueObject, that models the data with its range restrictions.
// Disclaimer: PHP is not my first language
class Quantity {
public function __construct(int $quantity) {
if ($quantity < 0) {
throw new \Exception("Quantity may not be lower than zero.");
}
$this.quantity = quantity
}
// ...
}
In the ease cases, Quantity, as understood by Orders::changeOrderLineQuantity(...) is the same domain concept as Quantity as understood by OrderLineQuantityChangedEvent(...) is the same domain concept as Quantity as understood by OrderLine::changeQuantity(...), and therefore you can re-use the same type everywhere; the type checker therefore ensures that the correct constraints are satisfied.
Edit
As noted by Eben Roux in the comments to this question, Quantity here should not be understood to be some universal, general-purpose type. It is instead specific to the context of Orders and OrderLines, and other parts of the code that share the same constraints for the same reason.
A complete solution might have several different Quantity types in different namespaces.
The requirement is to store the hardcoded value for varchar which is in an entity file(.eti). I tried adding to the default option but it is not reflecting.
Default option works well with boolean values (true/false), typelists (you can choose a default typecode), monetary amounts too, but it looks like it is not allowed to specify a default varchar.
Therefore the easiest way would be to create a preupdate rule which inserts that default value every time when you create a new record in the database.
Preupdate rule example:
#gw.rules.RuleName("YourEntityAssignDefaultValue")
internal class YourEntityAssignDefaultValueRule {
static function doCondition(yourEntity : entity.YourEntity) : boolean {
return yourEntity.New
}
static function doAction(yourEntity : entity.YourEntity, actions : gw.rules.Action) {
yourEntity.yourColumn = "defaultValue"
}
}
you can achieve through getter and setter properties in an appropriate enhancement class.
public property get PolicyNumber(): String {
return this.PolicyNumber }
and somewhere class you must be assigned the value to the PolicyNumber field then it will reflect.
Consider the following situation:
I got a title which is either 'A' when the type of an object is 'someType', or is empty apart from that. That is:
title="#{type eq 'someType' ? 'A' : ''}"
If the string returned by type changes, I won't get any errors. To be precise, the tooltip will be visible no longer, whilst I get no warnings on this. The important thing is, that type is not in my responsibility, but instead in another project. I defined an enum to use this in my code, which represents the values that type can receive.
public enum Type {
SOME_TYPE("someType"), //
ANOTHER_TYPE("anotherType");
String descr;
private Types(String descr) {
this.descr= descr;
}
}
Do you have any best practices here?