├── .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 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.saga-flow-projects/core.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.saga-flow-projects/core/1.0.0) 2 | 3 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=saga-flow-projects_saga-flow&metric=alert_status)](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 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 | --------------------------------------------------------------------------------