Merge branch 'master' of https://github.com/besok/java-design-patterns
This commit is contained in:
commit
51b444dccf
2
pom.xml
2
pom.xml
@ -183,6 +183,8 @@
|
|||||||
<module>data-locality</module>
|
<module>data-locality</module>
|
||||||
<module>subclass-sandbox</module>
|
<module>subclass-sandbox</module>
|
||||||
<module>circuit-breaker</module>
|
<module>circuit-breaker</module>
|
||||||
|
<module>role-object</module>
|
||||||
|
<module>saga</module>
|
||||||
<module>double-buffer</module>
|
<module>double-buffer</module>
|
||||||
<module>sharding</module>
|
<module>sharding</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
@ -43,6 +43,6 @@ public class InvestorRole extends CustomerRole {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String invest() {
|
public String invest() {
|
||||||
return String.format("Investor %s has invested %d dollars", name, amountToInvest);
|
return String.format("Investor %s has invested %d dollars", name, amountToInvest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
46
saga/README.md
Normal file
46
saga/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
layout: pattern
|
||||||
|
title: Saga
|
||||||
|
folder: saga
|
||||||
|
permalink: /patterns/saga/
|
||||||
|
categories: Behavioral
|
||||||
|
tags:
|
||||||
|
- Java
|
||||||
|
- Difficulty-Expert
|
||||||
|
- Idiom
|
||||||
|
- Distributed communication
|
||||||
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
This pattern has a similar goal with two-phase commit (XA transaction)
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
This pattern is used in distributed services to perform a group of operations atomically.
|
||||||
|
This is an analog of transaction in a database but in terms of microservices architecture this is executed
|
||||||
|
in a distributed environment
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason,
|
||||||
|
the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions.
|
||||||
|
There are two types of Saga:
|
||||||
|
|
||||||
|
- Choreography-Based Saga.
|
||||||
|
In this approach, there is no central orchestrator.
|
||||||
|
Each service participating in the Saga performs their transaction and publish events.
|
||||||
|
The other services act upon those events and perform their transactions.
|
||||||
|
Also, they may or not publish other events based on the situation.
|
||||||
|
|
||||||
|
- Orchestration-Based Saga
|
||||||
|
In this approach, there is a Saga orchestrator that manages all the transactions and directs
|
||||||
|
the participant services to execute local transactions based on events.
|
||||||
|
This orchestrator can also be though of as a Saga Manager.
|
||||||
|
|
||||||
|
## Applicability
|
||||||
|
Use the Saga pattern, if:
|
||||||
|
- you need to perform a group of operations related to different microservices atomically
|
||||||
|
- you need to rollback changes in different places in case of failure one of the operation
|
||||||
|
- you need to take care of data consistency in different places including different databases
|
||||||
|
- you can not use 2PC(two phase commit)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
- [Pattern: Saga](https://microservices.io/patterns/data/saga.html)
|
45
saga/pom.xml
Normal file
45
saga/pom.xml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<version>1.22.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>saga</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChoreographyChapter is an interface representing a contract for an external service.
|
||||||
|
* In that case, a service needs to make a decision what to do further
|
||||||
|
* hence the server needs to get all context representing {@link Saga}
|
||||||
|
*/
|
||||||
|
public interface ChoreographyChapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In that case, every method is responsible to make a decision on what to do then.
|
||||||
|
*
|
||||||
|
* @param saga incoming saga
|
||||||
|
* @return saga result
|
||||||
|
*/
|
||||||
|
Saga execute(Saga saga);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get name method.
|
||||||
|
* @return service name.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operation executed in general case.
|
||||||
|
*
|
||||||
|
* @param saga incoming saga
|
||||||
|
* @return result {@link Saga}
|
||||||
|
*/
|
||||||
|
Saga process(Saga saga);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operation executed in rollback case.
|
||||||
|
*
|
||||||
|
* @param saga incoming saga
|
||||||
|
* @return result {@link Saga}
|
||||||
|
*/
|
||||||
|
Saga rollback(Saga saga);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to book a fly.
|
||||||
|
*/
|
||||||
|
public class FlyBookingService extends Service {
|
||||||
|
public FlyBookingService(ServiceDiscoveryService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "booking a Fly";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to book a hotel.
|
||||||
|
*/
|
||||||
|
public class HotelBookingService extends Service {
|
||||||
|
public HotelBookingService(ServiceDiscoveryService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "booking a Hotel";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to init a new order.
|
||||||
|
*/
|
||||||
|
public class OrderService extends Service {
|
||||||
|
|
||||||
|
public OrderService(ServiceDiscoveryService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "init an order";
|
||||||
|
}
|
||||||
|
}
|
217
saga/src/main/java/com/iluwatar/saga/choreography/Saga.java
Normal file
217
saga/src/main/java/com/iluwatar/saga/choreography/Saga.java
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saga representation.
|
||||||
|
* Saga consists of chapters.
|
||||||
|
* Every ChoreographyChapter is executed a certain service.
|
||||||
|
*/
|
||||||
|
public class Saga {
|
||||||
|
|
||||||
|
private List<Chapter> chapters;
|
||||||
|
private int pos;
|
||||||
|
private boolean forward;
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
|
||||||
|
public static Saga create() {
|
||||||
|
return new Saga();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get resuzlt of saga.
|
||||||
|
*
|
||||||
|
* @return result of saga @see {@link SagaResult}
|
||||||
|
*/
|
||||||
|
public SagaResult getResult() {
|
||||||
|
if (finished) {
|
||||||
|
return forward
|
||||||
|
? SagaResult.FINISHED
|
||||||
|
: SagaResult.ROLLBACKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SagaResult.PROGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add chapter to saga.
|
||||||
|
* @param name chapter name
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public Saga chapter(String name) {
|
||||||
|
this.chapters.add(new Chapter(name));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set value to last chapter.
|
||||||
|
* @param value invalue
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public Saga setInValue(Object value) {
|
||||||
|
if (chapters.isEmpty()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
chapters.get(chapters.size() - 1).setInValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get value from current chapter.
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
public Object getCurrentValue() {
|
||||||
|
return chapters.get(pos).getInValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set value to current chapter.
|
||||||
|
* @param value to set
|
||||||
|
*/
|
||||||
|
public void setCurrentValue(Object value) {
|
||||||
|
chapters.get(pos).setInValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set status for current chapter.
|
||||||
|
* @param result to set
|
||||||
|
*/
|
||||||
|
public void setCurrentStatus(ChapterResult result) {
|
||||||
|
chapters.get(pos).setResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFinished(boolean finished) {
|
||||||
|
this.finished = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isForward() {
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
int forward() {
|
||||||
|
return ++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int back() {
|
||||||
|
this.forward = false;
|
||||||
|
return --pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Saga() {
|
||||||
|
this.chapters = new ArrayList<>();
|
||||||
|
this.pos = 0;
|
||||||
|
this.forward = true;
|
||||||
|
this.finished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chapter getCurrent() {
|
||||||
|
return chapters.get(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean isPresent() {
|
||||||
|
return pos >= 0 && pos < chapters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCurrentSuccess() {
|
||||||
|
return chapters.get(pos).isSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class presents a chapter status and incoming
|
||||||
|
* parameters(incoming parameter transforms to outcoming parameter).
|
||||||
|
*/
|
||||||
|
public static class Chapter {
|
||||||
|
private String name;
|
||||||
|
private ChapterResult result;
|
||||||
|
private Object inValue;
|
||||||
|
|
||||||
|
|
||||||
|
public Chapter(String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.result = ChapterResult.INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getInValue() {
|
||||||
|
return inValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInValue(Object object) {
|
||||||
|
this.inValue = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set result.
|
||||||
|
* @param result {@link ChapterResult}
|
||||||
|
*/
|
||||||
|
public void setResult(ChapterResult result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the result for chapter is good.
|
||||||
|
* @return true if is good otherwise bad
|
||||||
|
*/
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return result == ChapterResult.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* result for chapter.
|
||||||
|
*/
|
||||||
|
public enum ChapterResult {
|
||||||
|
INIT, SUCCESS, ROLLBACK
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* result for saga.
|
||||||
|
*/
|
||||||
|
public enum SagaResult {
|
||||||
|
PROGRESS, FINISHED, ROLLBACKED
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Saga{"
|
||||||
|
+ "chapters="
|
||||||
|
+ Arrays.toString(chapters.toArray())
|
||||||
|
+ ", pos="
|
||||||
|
+ pos
|
||||||
|
+ ", forward="
|
||||||
|
+ forward
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This pattern is used in distributed services to perform a group of operations atomically.
|
||||||
|
* This is an analog of transaction in a database but in terms
|
||||||
|
* of microservices architecture this is executed
|
||||||
|
* in a distributed environment
|
||||||
|
*
|
||||||
|
* <p>A saga is a sequence of local transactions in a certain context.
|
||||||
|
* If one transaction fails for some reason,
|
||||||
|
* the saga executes compensating transactions(rollbacks)
|
||||||
|
* to undo the impact of the preceding transactions.
|
||||||
|
*
|
||||||
|
* <p>In this approach, there are no mediators or orchestrators services.
|
||||||
|
* All chapters are handled and moved by services manually.
|
||||||
|
*
|
||||||
|
* <p>The major difference with choreography saga is an ability to handle crashed services
|
||||||
|
* (otherwise in choreography services very hard to prevent a saga
|
||||||
|
* if one of them has been crashed)
|
||||||
|
*
|
||||||
|
* @see com.iluwatar.saga.choreography.Saga
|
||||||
|
* @see Service
|
||||||
|
*/
|
||||||
|
public class SagaApplication {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main method.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ServiceDiscoveryService sd = serviceDiscovery();
|
||||||
|
ChoreographyChapter service = sd.findAny();
|
||||||
|
Saga goodOrderSaga = service.execute(newSaga("good_order"));
|
||||||
|
Saga badOrderSaga = service.execute(newSaga("bad_order"));
|
||||||
|
LOGGER.info("orders: goodOrder is {}, badOrder is {}",
|
||||||
|
goodOrderSaga.getResult(), badOrderSaga.getResult());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Saga newSaga(Object value) {
|
||||||
|
return Saga
|
||||||
|
.create()
|
||||||
|
.chapter("init an order").setInValue(value)
|
||||||
|
.chapter("booking a Fly")
|
||||||
|
.chapter("booking a Hotel")
|
||||||
|
.chapter("withdrawing Money");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceDiscoveryService serviceDiscovery() {
|
||||||
|
ServiceDiscoveryService sd = new ServiceDiscoveryService();
|
||||||
|
return sd
|
||||||
|
.discover(new OrderService(sd))
|
||||||
|
.discover(new FlyBookingService(sd))
|
||||||
|
.discover(new HotelBookingService(sd))
|
||||||
|
.discover(new WithdrawMoneyService(sd));
|
||||||
|
}
|
||||||
|
}
|
113
saga/src/main/java/com/iluwatar/saga/choreography/Service.java
Normal file
113
saga/src/main/java/com/iluwatar/saga/choreography/Service.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common abstraction class representing services.
|
||||||
|
* implementing a general contract @see {@link ChoreographyChapter}
|
||||||
|
*/
|
||||||
|
public abstract class Service implements ChoreographyChapter {
|
||||||
|
protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
|
||||||
|
|
||||||
|
private final ServiceDiscoveryService sd;
|
||||||
|
|
||||||
|
public Service(ServiceDiscoveryService service) {
|
||||||
|
this.sd = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Saga execute(Saga saga) {
|
||||||
|
Saga nextSaga = saga;
|
||||||
|
Object nextVal;
|
||||||
|
String chapterName = saga.getCurrent().getName();
|
||||||
|
if (chapterName.equals(getName())) {
|
||||||
|
if (saga.isForward()) {
|
||||||
|
nextSaga = process(saga);
|
||||||
|
nextVal = nextSaga.getCurrentValue();
|
||||||
|
if (nextSaga.isCurrentSuccess()) {
|
||||||
|
nextSaga.forward();
|
||||||
|
} else {
|
||||||
|
nextSaga.back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextSaga = rollback(saga);
|
||||||
|
nextVal = nextSaga.getCurrentValue();
|
||||||
|
nextSaga.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSagaFinished(nextSaga)) {
|
||||||
|
return nextSaga;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSaga.setCurrentValue(nextVal);
|
||||||
|
}
|
||||||
|
Saga finalNextSaga = nextSaga;
|
||||||
|
|
||||||
|
return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga))
|
||||||
|
.orElseThrow(serviceNotFoundException(chapterName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Supplier<RuntimeException> serviceNotFoundException(String chServiceName) {
|
||||||
|
return () -> new RuntimeException(
|
||||||
|
String.format("the service %s has not been found", chServiceName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Saga process(Saga saga) {
|
||||||
|
Object inValue = saga.getCurrentValue();
|
||||||
|
LOGGER.info("The chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been stored or calculated successfully",
|
||||||
|
getName(), inValue);
|
||||||
|
saga.setCurrentStatus(Saga.ChapterResult.SUCCESS);
|
||||||
|
saga.setCurrentValue(inValue);
|
||||||
|
return saga;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Saga rollback(Saga saga) {
|
||||||
|
Object inValue = saga.getCurrentValue();
|
||||||
|
LOGGER.info("The Rollback for a chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been rollbacked successfully",
|
||||||
|
getName(), inValue);
|
||||||
|
|
||||||
|
saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
|
||||||
|
saga.setCurrentValue(inValue);
|
||||||
|
return saga;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSagaFinished(Saga saga) {
|
||||||
|
if (!saga.isPresent()) {
|
||||||
|
saga.setFinished(true);
|
||||||
|
LOGGER.info(" the saga has been finished with {} status", saga.getResult());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class representing a service discovery pattern.
|
||||||
|
*/
|
||||||
|
public class ServiceDiscoveryService {
|
||||||
|
private Map<String, ChoreographyChapter> services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find any service.
|
||||||
|
*
|
||||||
|
* @return any service
|
||||||
|
* @throws NoSuchElementException if no elements further
|
||||||
|
*/
|
||||||
|
public ChoreographyChapter findAny() {
|
||||||
|
return services.values().iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ChoreographyChapter> find(String service) {
|
||||||
|
return Optional.ofNullable(services.getOrDefault(service, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceDiscoveryService discover(ChoreographyChapter chapterService) {
|
||||||
|
services.put(chapterService.getName(), chapterService);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceDiscoveryService() {
|
||||||
|
this.services = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to withdraw a money.
|
||||||
|
*/
|
||||||
|
public class WithdrawMoneyService extends Service {
|
||||||
|
|
||||||
|
public WithdrawMoneyService(ServiceDiscoveryService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "withdrawing Money";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Saga process(Saga saga) {
|
||||||
|
Object inValue = saga.getCurrentValue();
|
||||||
|
|
||||||
|
if (inValue.equals("bad_order")) {
|
||||||
|
LOGGER.info("The chapter '{}' has been started. But the exception has been raised."
|
||||||
|
+ "The rollback is about to start",
|
||||||
|
getName(), inValue);
|
||||||
|
saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
|
||||||
|
return saga;
|
||||||
|
}
|
||||||
|
return super.process(saga);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executing result for chapter.
|
||||||
|
*
|
||||||
|
* @param <K> incoming value
|
||||||
|
*/
|
||||||
|
public class ChapterResult<K> {
|
||||||
|
private K value;
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
public K getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChapterResult(K value, State state) {
|
||||||
|
this.value = value;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return state == State.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K> ChapterResult<K> success(K val) {
|
||||||
|
return new ChapterResult<>(val, State.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K> ChapterResult<K> failure(K val) {
|
||||||
|
return new ChapterResult<>(val, State.FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* state for chapter.
|
||||||
|
*/
|
||||||
|
public enum State {
|
||||||
|
SUCCESS, FAILURE
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to book a fly.
|
||||||
|
*/
|
||||||
|
public class FlyBookingService extends Service<String> {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "booking a Fly";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to book a hotel.
|
||||||
|
*/
|
||||||
|
public class HotelBookingService extends Service<String> {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "booking a Hotel";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<String> rollback(String value) {
|
||||||
|
if (value.equals("crashed_order")) {
|
||||||
|
LOGGER.info("The Rollback for a chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been failed.The saga has been crashed.",
|
||||||
|
getName(), value);
|
||||||
|
|
||||||
|
return ChapterResult.failure(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info("The Rollback for a chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been rollbacked successfully",
|
||||||
|
getName(), value);
|
||||||
|
|
||||||
|
return super.rollback(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChoreographyChapter is an interface representing a contract for an external service.
|
||||||
|
*
|
||||||
|
* @param <K> is type for passing params
|
||||||
|
*/
|
||||||
|
public interface OrchestrationChapter<K> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method get name.
|
||||||
|
* @return service name.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operation executed in general case.
|
||||||
|
*
|
||||||
|
* @param value incoming value
|
||||||
|
* @return result {@link ChapterResult}
|
||||||
|
*/
|
||||||
|
ChapterResult<K> process(K value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operation executed in rollback case.
|
||||||
|
*
|
||||||
|
* @param value incoming value
|
||||||
|
* @return result {@link ChapterResult}
|
||||||
|
*/
|
||||||
|
ChapterResult<K> rollback(K value);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to init a new order.
|
||||||
|
*/
|
||||||
|
public class OrderService extends Service<String> {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "init an order";
|
||||||
|
}
|
||||||
|
}
|
84
saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
Normal file
84
saga/src/main/java/com/iluwatar/saga/orchestration/Saga.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saga representation.
|
||||||
|
* Saga consists of chapters.
|
||||||
|
* Every ChoreographyChapter is executed by a certain service.
|
||||||
|
*/
|
||||||
|
public class Saga {
|
||||||
|
|
||||||
|
private List<Chapter> chapters;
|
||||||
|
|
||||||
|
|
||||||
|
private Saga() {
|
||||||
|
this.chapters = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Saga chapter(String name) {
|
||||||
|
this.chapters.add(new Chapter(name));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Chapter get(int idx) {
|
||||||
|
return chapters.get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPresent(int idx) {
|
||||||
|
return idx >= 0 && idx < chapters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Saga create() {
|
||||||
|
return new Saga();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* result for saga.
|
||||||
|
*/
|
||||||
|
public enum Result {
|
||||||
|
FINISHED, ROLLBACK, CRASHED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class represents chapter name.
|
||||||
|
*/
|
||||||
|
public static class Chapter {
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public Chapter(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This pattern is used in distributed services to perform
|
||||||
|
* a group of operations atomically.
|
||||||
|
* This is an analog of transaction in a database but in terms
|
||||||
|
* of microservices architecture this is executed
|
||||||
|
* in a distributed environment
|
||||||
|
*
|
||||||
|
* <p>A saga is a sequence of local transactions in a certain context.
|
||||||
|
* If one transaction fails for some reason,
|
||||||
|
* the saga executes compensating transactions(rollbacks)
|
||||||
|
* to undo the impact of the preceding transactions.
|
||||||
|
*
|
||||||
|
* <p>In this approach, there is an orchestrator @see {@link SagaOrchestrator}
|
||||||
|
* that manages all the transactions and directs
|
||||||
|
* the participant services to execute local transactions based on events.
|
||||||
|
* The major difference with choreography saga is an ability to handle crashed services
|
||||||
|
* (otherwise in choreography services very hard to prevent a saga
|
||||||
|
* if one of them has been crashed)
|
||||||
|
*
|
||||||
|
* @see Saga
|
||||||
|
* @see SagaOrchestrator
|
||||||
|
* @see Service
|
||||||
|
*/
|
||||||
|
public class SagaApplication {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SagaApplication.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method to show common saga logic.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
|
||||||
|
|
||||||
|
Saga.Result goodOrder = sagaOrchestrator.execute("good_order");
|
||||||
|
Saga.Result badOrder = sagaOrchestrator.execute("bad_order");
|
||||||
|
Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order");
|
||||||
|
|
||||||
|
LOGGER.info("orders: goodOrder is {}, badOrder is {},crashedOrder is {}",
|
||||||
|
goodOrder, badOrder, crashedOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Saga newSaga() {
|
||||||
|
return Saga
|
||||||
|
.create()
|
||||||
|
.chapter("init an order")
|
||||||
|
.chapter("booking a Fly")
|
||||||
|
.chapter("booking a Hotel")
|
||||||
|
.chapter("withdrawing Money");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceDiscoveryService serviceDiscovery() {
|
||||||
|
return
|
||||||
|
new ServiceDiscoveryService()
|
||||||
|
.discover(new OrderService())
|
||||||
|
.discover(new FlyBookingService())
|
||||||
|
.discover(new HotelBookingService())
|
||||||
|
.discover(new WithdrawMoneyService());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import static com.iluwatar.saga.orchestration.Saga.Result.CRASHED;
|
||||||
|
import static com.iluwatar.saga.orchestration.Saga.Result.FINISHED;
|
||||||
|
import static com.iluwatar.saga.orchestration.Saga.Result.ROLLBACK;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The orchestrator that manages all the transactions and directs
|
||||||
|
* the participant services to execute local transactions based on events.
|
||||||
|
*/
|
||||||
|
public class SagaOrchestrator {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SagaOrchestrator.class);
|
||||||
|
private final Saga saga;
|
||||||
|
private final ServiceDiscoveryService sd;
|
||||||
|
private final CurrentState state;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new service to orchetrate sagas.
|
||||||
|
* @param saga saga to process
|
||||||
|
* @param sd service discovery @see {@link ServiceDiscoveryService}
|
||||||
|
*/
|
||||||
|
public SagaOrchestrator(Saga saga, ServiceDiscoveryService sd) {
|
||||||
|
this.saga = saga;
|
||||||
|
this.sd = sd;
|
||||||
|
this.state = new CurrentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pipeline to execute saga process/story.
|
||||||
|
*
|
||||||
|
* @param value incoming value
|
||||||
|
* @param <K> type for incoming value
|
||||||
|
* @return result @see {@link Saga.Result}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <K> Saga.Result execute(K value) {
|
||||||
|
state.cleanUp();
|
||||||
|
LOGGER.info(" The new saga is about to start");
|
||||||
|
Saga.Result result = FINISHED;
|
||||||
|
K tempVal = value;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int next = state.current();
|
||||||
|
Saga.Chapter ch = saga.get(next);
|
||||||
|
Optional<OrchestrationChapter> srvOpt = sd.find(ch.name);
|
||||||
|
|
||||||
|
if (!srvOpt.isPresent()) {
|
||||||
|
state.directionToBack();
|
||||||
|
state.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OrchestrationChapter srv = srvOpt.get();
|
||||||
|
|
||||||
|
if (state.isForward()) {
|
||||||
|
ChapterResult processRes = srv.process(tempVal);
|
||||||
|
if (processRes.isSuccess()) {
|
||||||
|
next = state.forward();
|
||||||
|
tempVal = (K) processRes.getValue();
|
||||||
|
} else {
|
||||||
|
state.directionToBack();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ChapterResult rlRes = srv.rollback(tempVal);
|
||||||
|
if (rlRes.isSuccess()) {
|
||||||
|
next = state.back();
|
||||||
|
tempVal = (K) rlRes.getValue();
|
||||||
|
} else {
|
||||||
|
result = CRASHED;
|
||||||
|
next = state.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!saga.isPresent(next)) {
|
||||||
|
return state.isForward() ? FINISHED : result == CRASHED ? CRASHED : ROLLBACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class CurrentState {
|
||||||
|
int currentNumber;
|
||||||
|
boolean isForward;
|
||||||
|
|
||||||
|
void cleanUp() {
|
||||||
|
currentNumber = 0;
|
||||||
|
isForward = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentState() {
|
||||||
|
this.currentNumber = 0;
|
||||||
|
this.isForward = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean isForward() {
|
||||||
|
return isForward;
|
||||||
|
}
|
||||||
|
|
||||||
|
void directionToBack() {
|
||||||
|
isForward = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int forward() {
|
||||||
|
return ++currentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
int back() {
|
||||||
|
return --currentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
int current() {
|
||||||
|
return currentNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common abstraction class representing services.
|
||||||
|
* implementing a general contract @see {@link OrchestrationChapter}
|
||||||
|
*
|
||||||
|
* @param <K> type of incoming param
|
||||||
|
*/
|
||||||
|
public abstract class Service<K> implements OrchestrationChapter<K> {
|
||||||
|
protected static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract String getName();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<K> process(K value) {
|
||||||
|
LOGGER.info("The chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been stored or calculated successfully",
|
||||||
|
getName(), value);
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<K> rollback(K value) {
|
||||||
|
LOGGER.info("The Rollback for a chapter '{}' has been started. "
|
||||||
|
+ "The data {} has been rollbacked successfully",
|
||||||
|
getName(), value);
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class representing a service discovery pattern.
|
||||||
|
*/
|
||||||
|
public class ServiceDiscoveryService {
|
||||||
|
private Map<String, OrchestrationChapter<?>> services;
|
||||||
|
|
||||||
|
public Optional<OrchestrationChapter> find(String service) {
|
||||||
|
return Optional.ofNullable(services.getOrDefault(service, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceDiscoveryService discover(OrchestrationChapter<?> orchestrationChapterService) {
|
||||||
|
services.put(orchestrationChapterService.getName(), orchestrationChapterService);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceDiscoveryService() {
|
||||||
|
this.services = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a service to withdraw a money.
|
||||||
|
*/
|
||||||
|
public class WithdrawMoneyService extends Service<String> {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "withdrawing Money";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<String> process(String value) {
|
||||||
|
if (value.equals("bad_order") || value.equals("crashed_order")) {
|
||||||
|
LOGGER.info("The chapter '{}' has been started. But the exception has been raised."
|
||||||
|
+ "The rollback is about to start",
|
||||||
|
getName(), value);
|
||||||
|
return ChapterResult.failure(value);
|
||||||
|
}
|
||||||
|
return super.process(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import com.iluwatar.saga.orchestration.SagaApplication;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* empty test
|
||||||
|
*/
|
||||||
|
public class SagaApplicationTest {
|
||||||
|
@Test
|
||||||
|
public void mainTest() {
|
||||||
|
SagaApplication.main(new String[]{});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.saga.choreography;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test to check choreography saga
|
||||||
|
*/
|
||||||
|
public class SagaChoreographyTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void executeTest() {
|
||||||
|
ServiceDiscoveryService sd = serviceDiscovery();
|
||||||
|
ChoreographyChapter service = sd.findAny();
|
||||||
|
Saga badOrderSaga = service.execute(newSaga("bad_order"));
|
||||||
|
Saga goodOrderSaga = service.execute(newSaga("good_order"));
|
||||||
|
|
||||||
|
Assert.assertEquals(badOrderSaga.getResult(), Saga.SagaResult.ROLLBACKED);
|
||||||
|
Assert.assertEquals(goodOrderSaga.getResult(), Saga.SagaResult.FINISHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Saga newSaga(Object value) {
|
||||||
|
return Saga
|
||||||
|
.create()
|
||||||
|
.chapter("init an order").setInValue(value)
|
||||||
|
.chapter("booking a Fly")
|
||||||
|
.chapter("booking a Hotel")
|
||||||
|
.chapter("withdrawing Money");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceDiscoveryService serviceDiscovery() {
|
||||||
|
ServiceDiscoveryService sd = new ServiceDiscoveryService();
|
||||||
|
return sd
|
||||||
|
.discover(new OrderService(sd))
|
||||||
|
.discover(new FlyBookingService(sd))
|
||||||
|
.discover(new HotelBookingService(sd))
|
||||||
|
.discover(new WithdrawMoneyService(sd));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* empty test
|
||||||
|
*/
|
||||||
|
public class SagaApplicationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mainTest() {
|
||||||
|
SagaApplication.main(new String[]{});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test to test orchestration logic
|
||||||
|
*/
|
||||||
|
public class SagaOrchestratorInternallyTest {
|
||||||
|
|
||||||
|
private List<String> records = new ArrayList<>();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void executeTest() {
|
||||||
|
SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
|
||||||
|
Saga.Result result = sagaOrchestrator.execute(1);
|
||||||
|
Assert.assertEquals(result, Saga.Result.ROLLBACK);
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
records.toArray(new String[]{}),
|
||||||
|
new String[]{"+1", "+2", "+3", "+4", "-4", "-3", "-2", "-1"});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Saga newSaga() {
|
||||||
|
return Saga
|
||||||
|
.create()
|
||||||
|
.chapter("1")
|
||||||
|
.chapter("2")
|
||||||
|
.chapter("3")
|
||||||
|
.chapter("4");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceDiscoveryService serviceDiscovery() {
|
||||||
|
return
|
||||||
|
new ServiceDiscoveryService()
|
||||||
|
.discover(new Service1())
|
||||||
|
.discover(new Service2())
|
||||||
|
.discover(new Service3())
|
||||||
|
.discover(new Service4());
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service1 extends Service<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> process(Integer value) {
|
||||||
|
records.add("+1");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> rollback(Integer value) {
|
||||||
|
records.add("-1");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service2 extends Service<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "2";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> process(Integer value) {
|
||||||
|
records.add("+2");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> rollback(Integer value) {
|
||||||
|
records.add("-2");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service3 extends Service<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "3";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> process(Integer value) {
|
||||||
|
records.add("+3");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> rollback(Integer value) {
|
||||||
|
records.add("-3");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service4 extends Service<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "4";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> process(Integer value) {
|
||||||
|
records.add("+4");
|
||||||
|
return ChapterResult.failure(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChapterResult<Integer> rollback(Integer value) {
|
||||||
|
records.add("-4");
|
||||||
|
return ChapterResult.success(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2019 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.saga.orchestration;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test to check general logic
|
||||||
|
*/
|
||||||
|
public class SagaOrchestratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void execute() {
|
||||||
|
SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
|
||||||
|
Saga.Result badOrder = sagaOrchestrator.execute("bad_order");
|
||||||
|
Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order");
|
||||||
|
|
||||||
|
Assert.assertEquals(badOrder, Saga.Result.ROLLBACK);
|
||||||
|
Assert.assertEquals(crashedOrder, Saga.Result.CRASHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Saga newSaga() {
|
||||||
|
return Saga
|
||||||
|
.create()
|
||||||
|
.chapter("init an order")
|
||||||
|
.chapter("booking a Fly")
|
||||||
|
.chapter("booking a Hotel")
|
||||||
|
.chapter("withdrawing Money");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceDiscoveryService serviceDiscovery() {
|
||||||
|
return
|
||||||
|
new ServiceDiscoveryService()
|
||||||
|
.discover(new OrderService())
|
||||||
|
.discover(new FlyBookingService())
|
||||||
|
.discover(new HotelBookingService())
|
||||||
|
.discover(new WithdrawMoneyService());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user