├── .gitignore
├── LICENSE.txt
├── README.md
├── core
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── sagaflow
│ └── core
│ ├── CompensationContext.java
│ ├── ErrorHandlingTransactionManager.java
│ ├── SagaContext.java
│ ├── SagaErrorHandlingStrategy.java
│ ├── SagaErrorNotHandledException.java
│ ├── SagaEventListener.java
│ ├── SagaException.java
│ ├── SagaMetrics.java
│ ├── SagaOrchestrator.java
│ ├── SagaResult.java
│ ├── SagaRetryManager.java
│ ├── SagaState.java
│ ├── SagaTimeoutManager.java
│ ├── StepExecutor.java
│ ├── StepStatus.java
│ ├── TransactionContext.java
│ ├── TransactionManager.java
│ └── steps
│ ├── AbstractSagaStep.java
│ ├── CompensationStep.java
│ └── SagaStep.java
├── deploy.sh
├── docs
├── advanced_features.md
├── eventstream.md
├── examples.md
├── index.md
└── sagaflow.md
├── event-stream
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── sagaflow
│ └── eventStream
│ ├── DomainEvent.java
│ ├── EventBus.java
│ ├── EventPublisher.java
│ ├── EventStreamSagaListener.java
│ ├── EventSubscriber.java
│ └── event
│ ├── BaseEvent.java
│ ├── SagaCompletedEvent.java
│ ├── SagaFailedEvent.java
│ ├── SagaStepCompletedEvent.java
│ └── SagaStepFailedEvent.java
├── examples
├── core-examles
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── sagaflow
│ │ └── core
│ │ └── example
│ │ ├── SagaFlowCoreApplication.java
│ │ └── steps
│ │ ├── PaymentCompensationStep.java
│ │ └── PaymentSagaStep.java
├── event-stream-examples
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── sagaflow
│ │ └── eventStream
│ │ └── examples
│ │ ├── EventStreamApplication.java
│ │ ├── ExampleStep1.java
│ │ └── ExampleStep2.java
└── pom.xml
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
14 | build/
15 | !**/src/main/**/build/
16 | !**/src/test/**/build/
17 | environment-specific/
18 |
19 | .idea
20 | *.iws
21 | *.iml
22 | *.ipr
23 | out/
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Andrii Korkoshko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://search.maven.org/artifact/io.github.saga-flow-projects/core/1.0.0)
2 |
3 | [](https://sonarcloud.io/dashboard?id=saga-flow-projects_saga-flow)
4 |
5 | # SagaFlow
6 |
7 | ## Overview
8 | [documentation](https://saga-flow-projects.github.io/saga-flow/)
9 |
10 | SagaFlow is a Java-based library designed to manage and execute distributed transactions using the Saga pattern. It provides a robust mechanism to handle retries and ensure consistency across multiple services.
11 |
12 |
13 | ## Features
14 |
15 | - **Saga Pattern Implementation**: Manage distributed transactions with ease.
16 | - **Retry Mechanism**: Configurable retry logic for handling transient failures.
17 | - **Logging**: Integrated logging using SLF4J.
18 |
19 | ## Getting Started
20 |
21 | ### Prerequisites
22 |
23 | - Java 17 or higher
24 | - Maven
25 |
26 | ### Installation
27 |
28 | Add the following dependency to your `pom.xml` file:
29 |
30 | ```xml
31 |
32 | io.github.saga-flow-projects
33 | core
34 | 1.0.0
35 |
36 | ```
37 |
38 | To correct the usage section and include the `SagaOrchestrator` as the main class, here is an updated example:
39 |
40 | ### Usage
41 |
42 | #### SagaOrchestrator
43 |
44 | To correct the usage section and include the `SagaOrchestrator` as the main class, here is an updated example:
45 |
46 | ### Usage
47 |
48 | To correct the usage section and include the `SagaOrchestrator` as the main class, here is an updated example:
49 |
50 | ### Usage
51 |
52 | #### SagaOrchestrator
53 |
54 | The `SagaOrchestrator` class is responsible for managing the execution of multiple `SagaStep` instances. It coordinates the steps and handles retries using the `SagaRetryManager`.
55 |
56 | ```java
57 | package com.github.sagaflow.core.example;
58 |
59 | import com.github.sagaflow.core.SagaContext;
60 | import com.github.sagaflow.core.SagaOrchestrator;
61 | import com.github.sagaflow.core.SagaResult;
62 | import com.github.sagaflow.core.example.steps.PaymentCompensationStep;
63 | import com.github.sagaflow.core.example.steps.PaymentSagaStep;
64 |
65 | public class SagaFlowCoreApplication {
66 |
67 | public static void main(String[] args) {
68 | SagaOrchestrator orchestrator = new SagaOrchestrator();
69 | SagaContext context = new SagaContext();
70 | context.setTransactionId("TX123456");
71 | context.setUserId("User001");
72 |
73 | // Add execution steps
74 | orchestrator.addStep(new PaymentSagaStep());
75 |
76 | // Add compensation steps (for rollback)
77 | orchestrator.addCompensationStep(new PaymentCompensationStep());
78 |
79 | // Execute the saga
80 | SagaResult result = orchestrator.executeSaga(context);
81 | System.out.println("Saga Result: " + result.getMessage());
82 | }
83 | }
84 | ```
85 |
86 |
87 | ## Contributing
88 |
89 | Contributions are welcome! Please fork the repository and submit a pull request.
90 |
91 | ## License
92 |
93 | This project is licensed under the MIT License. See the `LICENSE` file for details.
94 |
95 | ## Contact
96 |
97 | For any questions or suggestions, please open an issue on GitHub.
98 |
99 |
--------------------------------------------------------------------------------
/core/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.github.saga-flow-projects
6 | saga-flow-parent
7 | 1.0.0
8 |
9 |
10 | core
11 | core
12 | jar
13 |
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/CompensationContext.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class CompensationContext {
7 | private String transactionId;
8 | private String reason;
9 | // Any additional metadata needed for compensation
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/ErrorHandlingTransactionManager.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 |
5 | public class ErrorHandlingTransactionManager extends TransactionManager {
6 |
7 | private final SagaErrorHandlingStrategy errorStrategy;
8 |
9 | public ErrorHandlingTransactionManager(SagaErrorHandlingStrategy errorStrategy) {
10 | this.errorStrategy = errorStrategy;
11 | }
12 |
13 | @Override
14 | public void beginTransaction(SagaContext context) {
15 | super.beginTransaction(context);
16 | // Custom error handling initialization
17 | }
18 |
19 | public void handleError(SagaContext context, SagaStep step, Exception e) throws SagaErrorNotHandledException {
20 | errorStrategy.handle(context, step, e);
21 | }
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaContext.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 | import lombok.Data;
3 |
4 | @Data
5 | public class SagaContext {
6 | private String transactionId;
7 | private String userId;
8 | private TransactionContext transactionContext;
9 | private boolean timeoutExceeded = false; // Flag for timeout
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaErrorHandlingStrategy.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 |
5 | public interface SagaErrorHandlingStrategy {
6 | void handle(SagaContext context, SagaStep step, Exception exception) throws SagaErrorNotHandledException;
7 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaErrorNotHandledException.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | public class SagaErrorNotHandledException extends SagaException {
4 | public SagaErrorNotHandledException(String message) {
5 | super(message);
6 | }
7 |
8 | public SagaErrorNotHandledException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaEventListener.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 |
5 | public interface SagaEventListener {
6 | void onStepCompleted(SagaContext context, SagaStep step);
7 | void onStepFailed(SagaContext context, SagaStep step, Exception exception);
8 | void onSagaCompleted(SagaContext context);
9 | void onSagaFailed(SagaContext context, Exception exception);
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaException.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | public class SagaException extends Exception {
4 | public SagaException(String message) {
5 | super(message);
6 | }
7 |
8 | public SagaException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaMetrics.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 |
5 | public interface SagaMetrics {
6 |
7 | default void recordSuccess(SagaStep step){};
8 |
9 | default void recordFailure(SagaStep step){};
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaOrchestrator.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.CompensationStep;
4 | import com.github.sagaflow.core.steps.SagaStep;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.concurrent.ScheduledFuture;
11 | import java.util.concurrent.atomic.AtomicBoolean;
12 |
13 |
14 | public class SagaOrchestrator {
15 |
16 | private static final Logger logger = LoggerFactory.getLogger(SagaOrchestrator.class);
17 | private final List steps = new ArrayList<>();
18 | private final List compensationSteps = new ArrayList<>();
19 | private final StepExecutor stepExecutor;
20 | private final SagaMetrics sagaMetrics = new SagaMetrics() {
21 | };
22 | private final SagaState sagaState = new SagaState();
23 | private final SagaTimeoutManager timeoutManager = new SagaTimeoutManager(); // Added SagaTimeoutManager
24 | private final List listeners = new ArrayList<>();
25 |
26 |
27 | public SagaOrchestrator() {
28 | this.stepExecutor = new StepExecutor(
29 | new ErrorHandlingTransactionManager(
30 | (context, step, exception) -> {
31 | throw new SagaErrorNotHandledException("Error handling strategy not implemented.");
32 | }
33 | ),
34 | new SagaRetryManager(3)
35 | );
36 | }
37 |
38 | public void addEventListener(SagaEventListener listener) {
39 | listeners.add(listener);
40 | }
41 |
42 | public SagaOrchestrator(SagaErrorHandlingStrategy errorHandlingStrategy) {
43 | TransactionManager transactionManager = new ErrorHandlingTransactionManager(errorHandlingStrategy);
44 | this.stepExecutor = new StepExecutor(transactionManager, new SagaRetryManager(3));
45 | }
46 |
47 | public void addStep(SagaStep step) {
48 | steps.add(step);
49 | sagaState.updateStepStatus(step, StepStatus.PENDING);
50 | }
51 |
52 | public void addCompensationStep(CompensationStep compensationStep) {
53 | compensationSteps.add(compensationStep);
54 | }
55 |
56 | public SagaResult executeSaga(SagaContext context) {
57 | logger.info("Starting saga execution...");
58 | CompensationContext compensationContext = new CompensationContext();
59 | compensationContext.setTransactionId(context.getTransactionId());
60 |
61 | try {
62 | SagaResult context1 = executeSage(context, compensationContext);
63 | if (context1 != null) return context1;
64 | } finally {
65 | timeoutManager.stopScheduler();
66 | }
67 | notifySagaCompleted(context);
68 |
69 | return new SagaResult(true, "Saga executed successfully.");
70 | }
71 |
72 | private SagaResult executeSage(SagaContext context, CompensationContext compensationContext) {
73 | for (int i = 0; i < steps.size(); i++) {
74 | SagaStep step = steps.get(i);
75 | AtomicBoolean isCompleted = new AtomicBoolean(false);
76 | ScheduledFuture> timeoutTask = null;
77 | try {
78 | timeoutTask = timeoutManager.startTimeout(step, context, 10, isCompleted);
79 | stepExecutor.executeStep(step, context);
80 | isCompleted.set(true);
81 |
82 | if (timeoutTask != null && !timeoutTask.isDone()) {
83 | timeoutTask.cancel(true);
84 | }
85 | sagaMetrics.recordSuccess(step);
86 | notifyStepCompleted(context, step);
87 | sagaState.updateStepStatus(step, StepStatus.COMPLETED);
88 | } catch (SagaException e) {
89 | return haandleSagaError(context, e, step, compensationContext, timeoutTask, i);
90 | }
91 | }
92 | return null;
93 | }
94 |
95 | private SagaResult haandleSagaError(SagaContext context, SagaException e, SagaStep step, CompensationContext compensationContext, ScheduledFuture> timeoutTask, int i) {
96 | logger.error("Error executing step: {}, starting compensation.", step.getClass().getSimpleName(), e);
97 | sagaState.updateStepStatus(step, StepStatus.FAILED);
98 | sagaMetrics.recordFailure(step);
99 | notifyStepFailed(context, step, e);
100 |
101 | compensationContext.setReason(e.getMessage());
102 |
103 | if (timeoutTask != null && !timeoutTask.isDone()) {
104 | timeoutTask.cancel(true);
105 | }
106 |
107 | rollbackSaga(context, compensationContext, i);
108 | notifySagaFailed(context, e);
109 | return new SagaResult(false, e.getMessage());
110 | }
111 |
112 | private void rollbackSaga(SagaContext context, CompensationContext compensationContext, int failedStepIndex) {
113 | logger.info("Rolling back saga...");
114 | for (int i = failedStepIndex; i >= 0; i--) {
115 | if (i < compensationSteps.size()) {
116 | CompensationStep compensationStep = compensationSteps.get(i);
117 | compensationStep.compensate(context, compensationContext);
118 | }
119 | }
120 | }
121 |
122 | private void notifyStepCompleted(SagaContext context, SagaStep step) {
123 | for (SagaEventListener listener : listeners) {
124 | listener.onStepCompleted(context, step);
125 | }
126 | }
127 |
128 | private void notifyStepFailed(SagaContext context, SagaStep step, Exception exception) {
129 | for (SagaEventListener listener : listeners) {
130 | listener.onStepFailed(context, step, exception);
131 | }
132 | }
133 |
134 | private void notifySagaCompleted(SagaContext context) {
135 | for (SagaEventListener listener : listeners) {
136 | listener.onSagaCompleted(context);
137 | }
138 | }
139 |
140 | private void notifySagaFailed(SagaContext context, Exception exception) {
141 | for (SagaEventListener listener : listeners) {
142 | listener.onSagaFailed(context, exception);
143 | }
144 | }
145 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaResult.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import lombok.Value;
4 |
5 | @Value
6 | public class SagaResult {
7 |
8 | boolean success;
9 | String message;
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaRetryManager.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 | import lombok.Value;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | @Slf4j
8 | @Value
9 | public class SagaRetryManager {
10 |
11 | int maxRetries;
12 |
13 | public boolean retry(SagaStep step, SagaContext context) {
14 | for (int attempt = 1; attempt <= maxRetries; attempt++) {
15 | try {
16 | step.execute(context);
17 | return true;
18 | } catch (SagaException e) {
19 | log.info("Retry attempt {} failed for step: {}", attempt, step.getClass().getSimpleName());
20 | }
21 | }
22 | return false;
23 | }
24 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaState.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 | import lombok.Getter;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | @Getter
10 | public class SagaState {
11 |
12 | private final Map stepStatuses = new HashMap<>();
13 |
14 | public void updateStepStatus(SagaStep step, StepStatus status) {
15 | stepStatuses.put(step, status);
16 | }
17 |
18 | public boolean isComplete() {
19 | return stepStatuses.values().stream().allMatch(status -> status == StepStatus.COMPLETED);
20 | }
21 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/SagaTimeoutManager.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.ScheduledExecutorService;
5 | import java.util.concurrent.ScheduledFuture;
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | import com.github.sagaflow.core.steps.SagaStep;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | @Slf4j
13 | public class SagaTimeoutManager {
14 |
15 | private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
16 |
17 | public ScheduledFuture> startTimeout(SagaStep step, SagaContext context, long timeoutSeconds, AtomicBoolean isCompleted) {
18 | return scheduler.schedule(() -> {
19 | if (!isCompleted.get()) {
20 | log.warn("Timeout reached for step: {}, initiating action.", step.getClass().getSimpleName());
21 | // Here we simply log the timeout, but don't rollback directly
22 | context.setTimeoutExceeded(true); // Set a flag to notify the orchestrator
23 | }
24 | }, timeoutSeconds, TimeUnit.SECONDS);
25 | }
26 |
27 | public void stopScheduler() {
28 | scheduler.shutdown(); // Shut down the scheduler gracefully when no longer needed
29 | }
30 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/StepExecutor.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import com.github.sagaflow.core.steps.SagaStep;
4 |
5 | public class StepExecutor {
6 |
7 | private final TransactionManager transactionManager;
8 | private final SagaRetryManager retryManager;
9 |
10 | public StepExecutor(TransactionManager transactionManager, SagaRetryManager retryManager) {
11 | this.transactionManager = transactionManager;
12 | this.retryManager = retryManager;
13 | }
14 |
15 | public void executeStep(SagaStep step, SagaContext context) throws SagaException {
16 | transactionManager.beginTransaction(context);
17 | try {
18 | boolean success = retryManager.retry(step, context);
19 | if (!success) {
20 | throw new SagaException("Step execution failed after retries.");
21 | }
22 | } catch (Exception e) {
23 | if (transactionManager instanceof ErrorHandlingTransactionManager) {
24 | // Handle error using the error handling strategy
25 | try {
26 | ((ErrorHandlingTransactionManager) transactionManager).handleError(context, step, e);
27 | } catch (SagaErrorNotHandledException ex) {
28 | throw new SagaException("Error handling failed after retries: " + ex.getMessage());
29 | }
30 | } else {
31 | throw new SagaException("Step execution failed: " + e.getMessage());
32 | }
33 | } finally {
34 | transactionManager.endTransaction(context);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/StepStatus.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | public enum StepStatus {
4 | PENDING,
5 | COMPLETED,
6 | FAILED,
7 | ROLLBACK
8 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/TransactionContext.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class TransactionContext {
7 | private String transactionId;
8 | private long timestamp;
9 | private String status;
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/TransactionManager.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | @Slf4j
6 | public class TransactionManager {
7 |
8 |
9 | public void beginTransaction(SagaContext context) {
10 | TransactionContext transactionContext = new TransactionContext();
11 | transactionContext.setTransactionId(context.getTransactionId());
12 | transactionContext.setTimestamp(System.currentTimeMillis());
13 | context.setTransactionContext(transactionContext); // Set the transaction context
14 |
15 | System.out.println("Beginning transaction with ID: " + transactionContext.getTransactionId());
16 | }
17 |
18 | public void endTransaction(SagaContext context) {
19 | System.out.println("Ending transaction with ID: " + context.getTransactionContext().getTransactionId());
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/steps/AbstractSagaStep.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.steps;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaException;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | @Slf4j
8 | public abstract class AbstractSagaStep implements SagaStep {
9 |
10 | @Override
11 | public void execute(SagaContext context) throws SagaException {
12 | log.info("Executing step: {}", this.getClass().getSimpleName());
13 | doExecute(context);
14 | }
15 |
16 | @Override
17 | public void rollback(SagaContext context) {
18 | log.info("Rolling back step: {}", this.getClass().getSimpleName());
19 | doRollback(context);
20 | }
21 |
22 | protected abstract void doExecute(SagaContext context) throws SagaException;
23 | protected abstract void doRollback(SagaContext context);
24 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/steps/CompensationStep.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.steps;
2 |
3 | import com.github.sagaflow.core.CompensationContext;
4 | import com.github.sagaflow.core.SagaContext;
5 |
6 | public interface CompensationStep {
7 | void compensate(SagaContext context, CompensationContext compensationContext);
8 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/sagaflow/core/steps/SagaStep.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.steps;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaException;
5 |
6 | public interface SagaStep {
7 | void execute(SagaContext context) throws SagaException;
8 | void rollback(SagaContext context);
9 | }
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | mvn clean deploy -pl core -am -P release
--------------------------------------------------------------------------------
/docs/advanced_features.md:
--------------------------------------------------------------------------------
1 |
2 | #### `advanced_features.md` (Advanced Features)
3 |
4 | ```markdown
5 | # Advanced Features
6 |
7 | ## 1. Dependency Inversion
8 |
9 | The **SagaFlow** core does not depend on any extensions, including **EventStream**. It follows the **Dependency Inversion Principle (DIP)**, allowing extensions to integrate seamlessly through interfaces like `SagaEventListener`.
10 |
11 | ### Example: EventStream Integration with SagaFlow
12 |
13 | ```java
14 | SagaOrchestrator orchestrator = new SagaOrchestrator();
15 | EventStreamSagaListener sagaListener = new EventStreamSagaListener(new EventPublisher(eventBus));
16 |
17 | orchestrator.addEventListener(sagaListener);
18 | orchestrator.addStep(new ExampleStep1());
19 | orchestrator.addStep(new ExampleStep2());
20 |
21 | orchestrator.executeSaga(new SagaContext());
22 |
--------------------------------------------------------------------------------
/docs/eventstream.md:
--------------------------------------------------------------------------------
1 |
2 | #### `eventstream.md` (EventStream Documentation)
3 |
4 | ```markdown
5 | # EventStream Framework
6 |
7 | ## Overview
8 |
9 | EventStream is an extension to the SagaFlow framework, providing a domain event publisher/subscriber mechanism. It allows publishing domain events throughout a system and enables components to subscribe to specific or generic events.
10 |
11 | ### Key Components:
12 | - **EventBus**: Routes events between publishers and subscribers.
13 | - **EventPublisher**: Publishes events to the EventBus.
14 | - **EventSubscriber**: Listens for domain events.
15 | - **DomainEvent**: Base interface for all domain events.
16 |
17 | ### Advanced Features:
18 | - **Event Hierarchy**: Subscribers can listen for events based on superclass or interface types, making the system flexible for different event types.
19 | - **Dependency Inversion**: EventStream is designed as a decoupled extension of the core SagaFlow framework.
20 |
21 | ### Example Usage
22 |
23 | ```java
24 | EventBus eventBus = new EventBus();
25 | EventPublisher eventPublisher = new EventPublisher(eventBus);
26 |
27 | // Subscribe to DomainEvent
28 | eventBus.subscribe(new GenericEventSubscriber());
29 |
30 | // Publish an event
31 | OrderCreatedEvent event = new OrderCreatedEvent("12345");
32 | eventPublisher.publish(event);
33 |
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
1 | # Code Examples
2 |
3 | ## 1. SagaFlow Basic Example
4 |
5 | This example demonstrates how to create a basic saga with two steps and execute it using the **SagaFlow** framework.
6 |
7 | ### Code:
8 |
9 | ```java
10 | import com.sagaflow.core.*;
11 |
12 | public class SagaFlowBasicExample {
13 |
14 | public static void main(String[] args) {
15 | // Create a SagaOrchestrator
16 | SagaOrchestrator orchestrator = new SagaOrchestrator();
17 |
18 | // Define saga steps
19 | SagaStep step1 = new ExampleStep1();
20 | SagaStep step2 = new ExampleStep2();
21 |
22 | // Add steps to orchestrator
23 | orchestrator.addStep(step1);
24 | orchestrator.addStep(step2);
25 |
26 | // Create saga context
27 | SagaContext context = new SagaContext();
28 |
29 | // Execute the saga
30 | SagaResult result = orchestrator.executeSaga(context);
31 | System.out.println("Saga result: " + result.getMessage());
32 | }
33 | }
34 |
35 | // Define example steps
36 | class ExampleStep1 implements SagaStep {
37 | @Override
38 | public void execute(SagaContext context) throws SagaException {
39 | System.out.println("Executing Step 1");
40 | // Simulate step logic
41 | }
42 |
43 | @Override
44 | public void rollback(SagaContext context) {
45 | System.out.println("Rolling back Step 1");
46 | // Simulate rollback logic
47 | }
48 | }
49 |
50 | class ExampleStep2 implements SagaStep {
51 | @Override
52 | public void execute(SagaContext context) throws SagaException {
53 | System.out.println("Executing Step 2");
54 | // Simulate step logic
55 | }
56 |
57 | @Override
58 | public void rollback(SagaContext context) {
59 | System.out.println("Rolling back Step 2");
60 | // Simulate rollback logic
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # SagaFlow Framework Documentation
2 |
3 | Welcome to the official documentation for the **SagaFlow** framework. This framework is designed to orchestrate distributed transactions and sagas in a microservices architecture, with an optional extension for **EventStream**, a domain event publisher/subscriber mechanism.
4 |
5 | ## Table of Contents
6 | 1. [SagaFlow Overview](sagaflow.md)
7 | 2. [EventStream Overview](eventstream.md)
8 | 3. [Advanced Features](advanced_features.md)
9 | 4. [Code Examples](examples.md)
10 |
11 | For more information, visit the individual sections above.
--------------------------------------------------------------------------------
/docs/sagaflow.md:
--------------------------------------------------------------------------------
1 | # SagaFlow Framework
2 |
3 | ## Overview
4 |
5 | SagaFlow is a framework that orchestrates distributed transactions (sagas) in a microservices architecture. It allows for defining steps in a saga and handles success, failure, and compensation actions.
6 |
7 | ### Core Components:
8 | - **SagaOrchestrator**: Manages the execution of saga steps.
9 | - **SagaStep**: Represents individual steps in the saga.
10 | - **SagaContext**: Holds metadata about the current saga execution.
11 | - **StepExecutor**: Executes individual steps in the saga.
12 | - **ErrorHandlingTransactionManager**: Manages transactional behavior across saga steps.
13 |
14 | ### Example Usage
15 |
16 | Here’s how you define and execute a simple saga:
17 |
18 | ```java
19 | SagaOrchestrator orchestrator = new SagaOrchestrator();
20 | orchestrator.addStep(new ExampleStep1());
21 | orchestrator.addStep(new ExampleStep2());
22 |
23 | SagaContext context = new SagaContext();
24 | orchestrator.executeSaga(context);
25 |
--------------------------------------------------------------------------------
/event-stream/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.github.saga-flow-projects
6 | saga-flow-parent
7 | 1.0.0
8 |
9 |
10 | event-stream
11 | jar
12 |
13 |
14 |
15 | io.github.saga-flow-projects
16 | core
17 | ${project.version}
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/DomainEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream;
2 |
3 | import java.time.Instant;
4 |
5 | public interface DomainEvent {
6 | Instant getOccurredAt();
7 | String eventType();
8 | }
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/EventBus.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 | public class EventBus {
8 |
9 | private final Map, List>> subscribersMap = new HashMap<>();
10 |
11 | // Register subscribers for specific event types
12 | public void subscribe(EventSubscriber subscriber) {
13 | subscribersMap
14 | .computeIfAbsent(subscriber.subscribedToEventType(), k -> new ArrayList<>())
15 | .add(subscriber);
16 | }
17 |
18 | // Publish an event to all relevant subscribers, including subscribers to superclasses/interfaces
19 | public void publish(T event) {
20 | // Notify subscribers for the specific event type
21 | notifySubscribersForEventType(event.getClass(), event);
22 | Class>[] interfaces = event.getClass().getInterfaces();
23 | callInterfaces(event, interfaces);
24 |
25 | // Notify subscribers for parent types (superclasses/interfaces)
26 | Class> superclass = event.getClass().getSuperclass();
27 | while (superclass != null && DomainEvent.class.isAssignableFrom(superclass)) {
28 | notifySubscribersForEventType(superclass, event);
29 | callInterfaces(event, superclass.getInterfaces());
30 | superclass = superclass.getSuperclass();
31 | }
32 |
33 | // Notify subscribers for implemented interfaces
34 |
35 | }
36 |
37 | private void callInterfaces(T event, Class>[] interfaces) {
38 | for (Class> eventInterface : interfaces) {
39 | if (DomainEvent.class.isAssignableFrom(eventInterface)) {
40 | notifySubscribersForEventType(eventInterface, event);
41 | }
42 | }
43 | }
44 |
45 | // Helper method to notify subscribers for a given event type
46 | @SuppressWarnings("unchecked")
47 | private void notifySubscribersForEventType(Class> eventType, T event) {
48 | List> subscribers = subscribersMap.get(eventType);
49 | if (subscribers != null) {
50 | for (EventSubscriber extends DomainEvent> subscriber : subscribers) {
51 | ((EventSubscriber) subscriber).onEvent(event); // Notify each subscriber
52 | }
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/EventPublisher.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | public class EventPublisher {
7 |
8 | private static final Logger logger = LoggerFactory.getLogger(EventPublisher.class);
9 | private final EventBus eventBus;
10 |
11 | public EventPublisher(EventBus eventBus) {
12 | this.eventBus = eventBus;
13 | }
14 |
15 | public void publish(DomainEvent event) {
16 | logger.info("Publishing event: {}", event.eventType());
17 | eventBus.publish(event);
18 | }
19 | }
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/EventStreamSagaListener.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaEventListener;
5 | import com.github.sagaflow.core.steps.SagaStep;
6 | import com.github.sagaflow.eventStream.event.SagaCompletedEvent;
7 | import com.github.sagaflow.eventStream.event.SagaFailedEvent;
8 | import com.github.sagaflow.eventStream.event.SagaStepCompletedEvent;
9 | import com.github.sagaflow.eventStream.event.SagaStepFailedEvent;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class EventStreamSagaListener implements SagaEventListener {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(EventStreamSagaListener.class);
16 | private final EventPublisher eventPublisher;
17 |
18 | public EventStreamSagaListener(EventPublisher eventPublisher) {
19 | this.eventPublisher = eventPublisher;
20 | }
21 |
22 | @Override
23 | public void onStepCompleted(SagaContext context, SagaStep step) {
24 | logger.info("Step completed: {}", step.getClass().getSimpleName());
25 | eventPublisher.publish(new SagaStepCompletedEvent(step.getClass().getSimpleName()));
26 | }
27 |
28 | @Override
29 | public void onStepFailed(SagaContext context, SagaStep step, Exception exception) {
30 | logger.warn("Step failed: {}", step.getClass().getSimpleName());
31 | eventPublisher.publish(new SagaStepFailedEvent(step.getClass().getSimpleName(), exception));
32 | }
33 |
34 | @Override
35 | public void onSagaCompleted(SagaContext context) {
36 | logger.info("Saga completed successfully.");
37 | eventPublisher.publish(new SagaCompletedEvent(context.getTransactionId()));
38 | }
39 |
40 | @Override
41 | public void onSagaFailed(SagaContext context, Exception exception) {
42 | logger.error("Saga failed.", exception);
43 | eventPublisher.publish(new SagaFailedEvent(context.getTransactionId(), exception));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/EventSubscriber.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream;
2 |
3 | public interface EventSubscriber {
4 | void onEvent(T event);
5 | Class subscribedToEventType();
6 | }
7 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/event/BaseEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.event;
2 |
3 | import com.github.sagaflow.eventStream.DomainEvent;
4 | import lombok.Getter;
5 |
6 | import java.time.Instant;
7 |
8 |
9 | @Getter
10 | public abstract class BaseEvent implements DomainEvent {
11 |
12 | private final Instant occurredAt = Instant.now();
13 | private final String name;
14 | public BaseEvent(String name) {
15 | this.name = name;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/event/SagaCompletedEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.event;
2 |
3 | public class SagaCompletedEvent extends BaseEvent {
4 |
5 | public SagaCompletedEvent(String name) {
6 | super(name);
7 | }
8 |
9 | @Override
10 | public String eventType() {
11 | return "saga completed";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/event/SagaFailedEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.event;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public class SagaFailedEvent extends BaseEvent {
7 |
8 | private final Throwable cause;
9 |
10 | public SagaFailedEvent(String name, Throwable cause) {
11 | super(name);
12 | this.cause = cause;
13 | }
14 |
15 | @Override
16 | public String eventType() {
17 | return "saga failed";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/event/SagaStepCompletedEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.event;
2 |
3 | public class SagaStepCompletedEvent extends BaseEvent {
4 |
5 | public SagaStepCompletedEvent(String name) {
6 | super(name);
7 | }
8 |
9 | @Override
10 | public String eventType() {
11 | return "step completed";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/event-stream/src/main/java/com/github/sagaflow/eventStream/event/SagaStepFailedEvent.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.event;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public class SagaStepFailedEvent extends BaseEvent {
7 |
8 | private final Throwable cause;
9 |
10 | public SagaStepFailedEvent(String name, Throwable cause) {
11 | super(name);
12 | this.cause = cause;
13 | }
14 |
15 | @Override
16 | public String eventType() {
17 | return "step failed";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/core-examles/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.github.saga-flow-projects
6 | examples
7 | 1.0.0
8 |
9 | core-examles
10 |
11 |
12 |
13 |
14 | io.github.saga-flow-projects
15 | core
16 | ${project.version}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/core-examles/src/main/java/com/github/sagaflow/core/example/SagaFlowCoreApplication.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.example;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaOrchestrator;
5 | import com.github.sagaflow.core.SagaResult;
6 | import com.github.sagaflow.core.example.steps.PaymentCompensationStep;
7 | import com.github.sagaflow.core.example.steps.PaymentSagaStep;
8 |
9 | public class SagaFlowCoreApplication {
10 |
11 | public static void main(String[] args) {
12 | SagaOrchestrator orchestrator = new SagaOrchestrator();
13 | SagaContext context = new SagaContext();
14 | context.setTransactionId("TX123456");
15 | context.setUserId("User001");
16 |
17 | // Add execution steps
18 | orchestrator.addStep(new PaymentSagaStep());
19 |
20 | // Add compensation steps (for rollback)
21 | orchestrator.addCompensationStep(new PaymentCompensationStep());
22 |
23 | // Execute the saga
24 | SagaResult result = orchestrator.executeSaga(context);
25 | System.out.println("Saga Result: " + result.getMessage());
26 | }
27 | }
--------------------------------------------------------------------------------
/examples/core-examles/src/main/java/com/github/sagaflow/core/example/steps/PaymentCompensationStep.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.example.steps;
2 |
3 | import com.github.sagaflow.core.CompensationContext;
4 | import com.github.sagaflow.core.SagaContext;
5 | import com.github.sagaflow.core.steps.CompensationStep;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | @Slf4j
9 | public class PaymentCompensationStep implements CompensationStep {
10 |
11 | @Override
12 | public void compensate(SagaContext context, CompensationContext compensationContext) {
13 | log.info("Compensating payment for user: " + context.getUserId() + " due to: " + compensationContext.getReason());
14 | // Logic to reverse payment (e.g., refund the amount)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/core-examles/src/main/java/com/github/sagaflow/core/example/steps/PaymentSagaStep.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.core.example.steps;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaException;
5 | import com.github.sagaflow.core.steps.AbstractSagaStep;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | @Slf4j
9 | public class PaymentSagaStep extends AbstractSagaStep {
10 |
11 | @Override
12 | protected void doExecute(SagaContext context) throws SagaException {
13 | // Simulate payment processing
14 | log.info("Processing payment for user: {}", context.getUserId());
15 | if (Math.random() > 0.5) {
16 | throw new SagaException("Payment failed for user: " + context.getUserId());
17 | }
18 | log.info("Payment successful.");
19 | }
20 |
21 | @Override
22 | protected void doRollback(SagaContext context) {
23 | log.info("Rolling back payment for user: {}", context.getUserId());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/event-stream-examples/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.github.saga-flow-projects
6 | examples
7 | 1.0.0
8 |
9 | event-stream-examples
10 |
11 |
12 | io.github.saga-flow-projects
13 | event-stream
14 | ${project.version}
15 |
16 |
17 |
18 | io.github.saga-flow-projects
19 | core
20 | ${project.version}
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/event-stream-examples/src/main/java/com/github/sagaflow/eventStream/examples/EventStreamApplication.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.examples;
2 |
3 |
4 | import com.github.sagaflow.core.SagaContext;
5 | import com.github.sagaflow.eventStream.*;
6 | import com.github.sagaflow.core.SagaOrchestrator;
7 | import com.github.sagaflow.core.steps.SagaStep;
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 |
11 | @Slf4j
12 | public class EventStreamApplication {
13 |
14 | public static void main(String[] args) {
15 | // Create EventBus
16 | EventBus eventBus = new EventBus();
17 |
18 | // Create EventPublisher
19 | EventPublisher eventPublisher = new EventPublisher(eventBus);
20 |
21 | // Create EventStreamSagaListener
22 | EventStreamSagaListener sagaListener = new EventStreamSagaListener(eventPublisher);
23 |
24 | // Create SagaOrchestrator and register listener
25 | SagaOrchestrator orchestrator = new SagaOrchestrator();
26 | orchestrator.addEventListener(sagaListener);
27 |
28 | // Define Saga steps (example)
29 | SagaStep step1 = new ExampleStep1();
30 | SagaStep step2 = new ExampleStep2();
31 |
32 | // Add steps to orchestrator
33 | orchestrator.addStep(step1);
34 | orchestrator.addStep(step2);
35 |
36 | // Execute the saga
37 | SagaContext context = new SagaContext();
38 | // Subscribing and publishing events
39 | eventBus.subscribe(new EventSubscriber() {
40 | @Override
41 | public void onEvent(DomainEvent event) {
42 | log.info("Received event: " + event);
43 | }
44 |
45 | @Override
46 | public Class subscribedToEventType() {
47 | return DomainEvent.class;
48 | }
49 | });
50 |
51 |
52 | orchestrator.executeSaga(context);
53 |
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/examples/event-stream-examples/src/main/java/com/github/sagaflow/eventStream/examples/ExampleStep1.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.examples;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaException;
5 | import com.github.sagaflow.core.steps.SagaStep;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | @Slf4j
9 | public class ExampleStep1 implements SagaStep {
10 | @Override
11 | public void execute(SagaContext context) throws SagaException {
12 | log.info("Executing step: {}", this.getClass().getSimpleName());
13 | }
14 |
15 | @Override
16 | public void rollback(SagaContext context) {
17 | log.info("Rolling back step: {}", this.getClass().getSimpleName());
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/event-stream-examples/src/main/java/com/github/sagaflow/eventStream/examples/ExampleStep2.java:
--------------------------------------------------------------------------------
1 | package com.github.sagaflow.eventStream.examples;
2 |
3 | import com.github.sagaflow.core.SagaContext;
4 | import com.github.sagaflow.core.SagaException;
5 | import com.github.sagaflow.core.steps.SagaStep;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | @Slf4j
9 | public class ExampleStep2 implements SagaStep {
10 | @Override
11 | public void execute(SagaContext context) throws SagaException {
12 | log.info("Executing step: {}", this.getClass().getSimpleName());
13 | throw new SagaException("Failed to execute step: " + this.getClass().getSimpleName());
14 | }
15 |
16 | @Override
17 | public void rollback(SagaContext context) {
18 | log.info("Rolling back step: {}", this.getClass().getSimpleName());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.github.saga-flow-projects
6 | saga-flow-parent
7 | 1.0.0
8 |
9 |
10 | examples
11 | pom
12 |
13 |
14 | core-examles
15 | event-stream-examples
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 |
6 | io.github.saga-flow-projects
7 | saga-flow-parent
8 | 1.0.0
9 | pom
10 |
11 | core
12 | examples
13 | event-stream
14 |
15 |
16 | Saga Flow
17 | An open-source project for implementing saga patterns in microservices architecture.
18 | https://github.com/saga-flow-projects/saga-flow
19 |
20 |
21 |
22 | MIT License
23 | https://opensource.org/licenses/MIT
24 | repo
25 |
26 |
27 |
28 |
29 | https://github.com/saga-flow-projects/saga-flow
30 | scm:git:https://github.com/saga-flow-projects/saga-flow.git
31 | scm:git:https://github.com/saga-flow-projects/saga-flow.git
32 |
33 |
34 |
35 |
36 | Andrii Korkoshko
37 | andreydemosoft@gmail.com
38 |
39 |
40 |
41 |
42 | 17
43 | 1.18.34
44 | ${java.version}
45 | ${java.version}
46 |
47 |
48 |
49 |
50 |
51 | org.projectlombok
52 | lombok
53 | ${lombok.version}
54 | provided
55 |
56 |
57 |
58 | org.slf4j
59 | slf4j-simple
60 | 2.0.3
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | org.apache.maven.plugins
70 | maven-gpg-plugin
71 | 1.5
72 |
73 |
74 | sign-artifacts
75 | verify
76 |
77 | sign
78 |
79 |
80 |
81 |
82 |
83 | --pinentry-mode
84 | loopback
85 |
86 |
87 |
88 |
89 | org.apache.maven.plugins
90 | maven-compiler-plugin
91 | 3.10.1
92 |
93 | ${java.version}
94 | ${java.version}
95 |
96 |
97 | org.projectlombok
98 | lombok
99 | ${lombok.version}
100 |
101 |
102 |
103 |
104 |
105 | org.sonatype.central
106 | central-publishing-maven-plugin
107 | 0.5.0
108 | true
109 |
110 | central
111 |
112 |
113 |
114 | org.apache.maven.plugins
115 | maven-javadoc-plugin
116 | 3.3.0
117 |
118 |
119 |
120 | jar
121 |
122 |
123 |
124 |
125 |
126 | org.apache.maven.plugins
127 | maven-source-plugin
128 | 3.2.1
129 |
130 |
131 |
132 | jar
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------