diff --git a/ddd/src/main/java/com/euonia/application/ApplicationService.java b/ddd/src/main/java/com/euonia/application/ApplicationService.java new file mode 100644 index 0000000..a380ac1 --- /dev/null +++ b/ddd/src/main/java/com/euonia/application/ApplicationService.java @@ -0,0 +1,4 @@ +package com.euonia.application; + +public interface ApplicationService { +} diff --git a/ddd/src/main/java/com/euonia/application/BaseApplicationService.java b/ddd/src/main/java/com/euonia/application/BaseApplicationService.java new file mode 100644 index 0000000..ed09c01 --- /dev/null +++ b/ddd/src/main/java/com/euonia/application/BaseApplicationService.java @@ -0,0 +1,26 @@ +package com.euonia.application; + +import com.euonia.reflection.ServiceResolver; +import com.euonia.security.UserPrincipal; + +/** + * BaseApplicationService is an abstract class that provides common functionality for application services. + * It implements the ApplicationService interface and uses a ServiceResolver to access other services, such as UserPrincipal, which represents the currently authenticated user. + * This class can be extended by concrete application service implementations to inherit the ability to resolve services and access user information without needing to implement these features in each service. + */ +public abstract class BaseApplicationService implements ApplicationService { + + protected final ServiceResolver serviceResolver; + + protected BaseApplicationService(ServiceResolver serviceResolver) { + this.serviceResolver = serviceResolver; + } + + protected T getService(Class type) { + return (T) serviceResolver.getService(type); + } + + protected UserPrincipal getUser() { + return serviceResolver.getService(UserPrincipal.class); + } +} diff --git a/ddd/src/main/java/com/euonia/domain/Aggregate.java b/ddd/src/main/java/com/euonia/domain/Aggregate.java index d4bb2c6..cc0669d 100644 --- a/ddd/src/main/java/com/euonia/domain/Aggregate.java +++ b/ddd/src/main/java/com/euonia/domain/Aggregate.java @@ -1,4 +1,12 @@ package com.euonia.domain; +/** + * The Aggregate interface represents an aggregate root in a domain-driven design (DDD) context. + * An aggregate is a cluster of domain objects that can be treated as a single unit for data changes. + * The aggregate root is the main entity that controls access to the other entities within the aggregate and is responsible for enforcing the consistency of the aggregate as a whole. + * + * @param the type of the identifier for the aggregate + */ public interface Aggregate> extends Entity { + } diff --git a/ddd/src/main/java/com/euonia/domain/AggregateBase.java b/ddd/src/main/java/com/euonia/domain/AggregateBase.java index d9c7147..b27e37f 100644 --- a/ddd/src/main/java/com/euonia/domain/AggregateBase.java +++ b/ddd/src/main/java/com/euonia/domain/AggregateBase.java @@ -1,27 +1,57 @@ package com.euonia.domain; -import com.euonia.domain.event.DomainEventBase; +import com.euonia.domain.event.DomainEvent; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; -public abstract class AggregateBase> extends EntityBase implements Aggregate { - private final List events = new ArrayList<>(); +/** + * The AggregateBase class is an abstract base class for implementing the Aggregate interface in a domain-driven design (DDD) context. + * It provides a common implementation for managing domain events and their handlers within an aggregate root. + * An aggregate is a cluster of domain objects that can be treated as a single unit for data changes. + * The aggregate root is the main entity that controls access to the other entities within the aggregate and is responsible for enforcing the consistency of the aggregate as a whole. + * + * @param the type of the identifier for the aggregate, which must be comparable. + */ +public abstract class AggregateBase> extends EntityBase implements Aggregate, HasDomainEvents { + private final List events = new ArrayList<>(); + private final ConcurrentMap, List>> eventHandlers = new ConcurrentHashMap<>(); - public List getEvents() { + public void registerEvent(Class eventType, Consumer handler) { + eventHandlers.computeIfAbsent(eventType, k -> new ArrayList<>()) + .add(event -> handler.accept(eventType.cast(event))); + } + + @Override + public List getEvents() { return List.copyOf(events); } - protected void raiseEvent(E event) { + @Override + public void raiseEvent(E event) { + applyEvent(event); events.add(event); } - protected void clearEvents() { + @Override + public void applyEvent(E event) { + var handlers = eventHandlers.getOrDefault(event.getClass(), null); + if (handlers != null) { + handlers.forEach(handler -> handler.accept(event)); + } + } + + @Override + public void clearEvents() { events.clear(); } + @Override public void attachEvents() { - for (DomainEventBase event : events) { + for (DomainEvent event : events) { event.attach(this); } } diff --git a/ddd/src/main/java/com/euonia/domain/Entity.java b/ddd/src/main/java/com/euonia/domain/Entity.java index b11b730..8c90e7f 100644 --- a/ddd/src/main/java/com/euonia/domain/Entity.java +++ b/ddd/src/main/java/com/euonia/domain/Entity.java @@ -6,9 +6,27 @@ * They typically represent real-world concepts or objects in the system and are often used to model business entities or domain objects. */ public interface Entity> { + + /** + * Gets the identifier for the entity. The identifier is a unique value that distinguishes this entity from others. + * + * @return the unique identifier of the entity + */ ID getId(); + /** + * Sets the identifier for the entity. The identifier is a unique value that distinguishes this entity from others. + * + * @param id the unique identifier to set for this entity + */ void setId(ID id); - Object[] getKeys(); + /** + * Gets the keys that uniquely identify this entity. By default, it returns an array containing the entity's identifier. + * + * @return an array of objects representing the keys that uniquely identify this entity + */ + default Object[] getKeys() { + return new Object[]{getId()}; + } } diff --git a/ddd/src/main/java/com/euonia/domain/HasDomainEvents.java b/ddd/src/main/java/com/euonia/domain/HasDomainEvents.java new file mode 100644 index 0000000..0649879 --- /dev/null +++ b/ddd/src/main/java/com/euonia/domain/HasDomainEvents.java @@ -0,0 +1,57 @@ +package com.euonia.domain; + +import com.euonia.domain.event.DomainEvent; + +import java.util.List; +import java.util.function.Consumer; + +/** + * The HasDomainEvents interface defines the contract for an aggregate that can raise and manage domain events in a domain-driven design (DDD) context. + * An aggregate is a cluster of domain objects that can be treated as a single unit for data changes. + * The aggregate root is the main entity that controls access to the other entities within the aggregate and is responsible for enforcing the consistency of the aggregate as a whole. + * Domain events represent significant occurrences or changes in the state of the aggregate that are relevant to the business logic and can be used for event sourcing, auditing, or integration with other systems. + */ +public interface HasDomainEvents { + /** + * Gets the list of domain events that have been raised by this aggregate. + * Domain events represent significant occurrences or changes in the state of the aggregate that are relevant to the business logic and can be used for event sourcing, auditing, or integration with other systems. + * + * @return the list of domain events raised by this aggregate + */ + List getEvents(); + + /** + * Registers a handler for a specific type of domain event. The handler will be invoked when an event of the specified type is raised. + * + * @param eventType the type of domain event to register the handler for + * @param handler the handler to be invoked when the event is raised + * @param the type of the domain event + */ + void registerEvent(Class eventType, Consumer handler); + + /** + * Raises a domain event. The event will be processed by the registered handlers. + * + * @param event the domain event to raise + * @param the type of the domain event + */ + void raiseEvent(E event); + + /** + * Applies a domain event to the aggregate. This method is typically used to update the state of the aggregate based on the event. + * + * @param event the domain event to apply + * @param the type of the domain event + */ + void applyEvent(E event); + + /** + * Clears all domain events that have been raised by this aggregate. + */ + void clearEvents(); + + /** + * Attaches all domain events to the aggregate. This method is typically used to initialize the aggregate with existing events. + */ + void attachEvents(); +} diff --git a/ddd/src/main/java/com/euonia/usecase/UseCase.java b/ddd/src/main/java/com/euonia/usecase/UseCase.java new file mode 100644 index 0000000..7c9f33a --- /dev/null +++ b/ddd/src/main/java/com/euonia/usecase/UseCase.java @@ -0,0 +1,18 @@ +package com.euonia.usecase; + +/** + * Represents a use case in the application. A use case is a specific operation or action that can be performed within the system. + * It defines a contract for executing a particular business logic or functionality. + * + * @param the type of the input to the use case + * @param the type of the output of the use case + */ +public interface UseCase { + /** + * Executes the use case with the given input and returns the output. + * + * @param input the input data for the use case + * @return the output data from the use case + */ + O execute(I input); +} diff --git a/ddd/src/main/java/com/euonia/usecase/UseCaseFailure.java b/ddd/src/main/java/com/euonia/usecase/UseCaseFailure.java new file mode 100644 index 0000000..7c8038d --- /dev/null +++ b/ddd/src/main/java/com/euonia/usecase/UseCaseFailure.java @@ -0,0 +1,14 @@ +package com.euonia.usecase; + +/** + * Represents a failure output of a use case execution. + * This interface defines a method to handle errors that occur during the execution of a use case. + */ +public interface UseCaseFailure { + /** + * Indicates that the use case execution has failed with an error. + * + * @param throwable the error that caused the failure + */ + void error(Throwable throwable); +} diff --git a/ddd/src/main/java/com/euonia/usecase/UseCasePresenter.java b/ddd/src/main/java/com/euonia/usecase/UseCasePresenter.java new file mode 100644 index 0000000..a00cb4a --- /dev/null +++ b/ddd/src/main/java/com/euonia/usecase/UseCasePresenter.java @@ -0,0 +1,72 @@ +package com.euonia.usecase; + +import java.util.concurrent.Flow.*; +import java.util.concurrent.SubmissionPublisher; +import java.util.function.Consumer; + +/** + * A presenter that implements the output ports for a use case and allows subscribers to listen for success and failure events. + * + * @param the type of the successful output of the use case + */ +public class UseCasePresenter implements UseCaseSuccess, UseCaseFailure, AutoCloseable { + private final Publisher publisher = new SubmissionPublisher<>(); + + private O output; + + /** + * Subscribes to the presenter to receive success and failure events. + * + * @param onSuccess the consumer to handle successful output + * @param onFailure the consumer to handle errors + */ + public void subscribe(Consumer onSuccess, Consumer onFailure) { + publisher.subscribe(new Subscriber<>() { + + @Override + public void onSubscribe(Subscription subscription) { + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(O item) { + onSuccess.accept(item); + } + + @Override + public void onError(Throwable throwable) { + onFailure.accept(throwable); + } + + @Override + public void onComplete() { + // Handle completion if needed + } + }); + } + + @Override + public void success(O output) { + this.output = output; + ((SubmissionPublisher) publisher).submit(output); + } + + @Override + public void error(Throwable throwable) { + ((SubmissionPublisher) publisher).closeExceptionally(throwable); + } + + @Override + public void close() throws Exception { + ((SubmissionPublisher) publisher).close(); + } + + /** + * Returns the output of the use case execution if it was successful. + * + * @return the output of the use case execution + */ + public O getOutput() { + return output; + } +} diff --git a/ddd/src/main/java/com/euonia/usecase/UseCaseSuccess.java b/ddd/src/main/java/com/euonia/usecase/UseCaseSuccess.java new file mode 100644 index 0000000..45527b7 --- /dev/null +++ b/ddd/src/main/java/com/euonia/usecase/UseCaseSuccess.java @@ -0,0 +1,16 @@ +package com.euonia.usecase; + +/** + * Represents a successful output of a use case execution. + * This interface defines a method to handle the result of a successful use case execution. + * + * @param the type of the successful output + */ +public interface UseCaseSuccess { + /** + * Indicates that the use case execution has succeeded with a result. + * + * @param output the result of the successful execution + */ + void success(O output); +} diff --git a/osba/src/main/java/com/euonia/factory/BusinessObjectFactory.java b/osba/src/main/java/com/euonia/factory/BusinessObjectFactory.java index c632c36..ec620e6 100644 --- a/osba/src/main/java/com/euonia/factory/BusinessObjectFactory.java +++ b/osba/src/main/java/com/euonia/factory/BusinessObjectFactory.java @@ -5,12 +5,12 @@ import com.euonia.osba.*; import com.euonia.osba.abstracts.UseBusinessContext; import com.euonia.reflection.ObjectReflector; +import com.euonia.reflection.ServiceResolver; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Objects; -import java.util.function.Function; /** * BusinessObjectFactory is an implementation of the ObjectFactory interface that uses reflection to create, fetch, insert, update, save, execute, and delete business objects based on annotated factory methods. @@ -19,16 +19,16 @@ @SuppressWarnings("UnusedReturnValue") public class BusinessObjectFactory implements ObjectFactory { - private Function, ?> beanFactory; + private ServiceResolver resolver; /** * Configures the BusinessObjectFactory to use the provided bean factory for object instantiation. * - * @param beanFactory the bean factory function to use for creating objects + * @param resolver the ServiceResolver to be used for resolving services, including the bean factory * @return the current instance of BusinessObjectFactory */ - public BusinessObjectFactory use(Function, ?> beanFactory) { - this.beanFactory = beanFactory; + public BusinessObjectFactory use(ServiceResolver resolver) { + this.resolver = resolver; return this; } @@ -173,10 +173,10 @@ public void delete(Class type, Object... criteria) { @SuppressWarnings("unchecked") private T getObjectInstance(Class type) { T object = PriorityValueFinder.find(queue -> { - if (beanFactory != null) { + if (resolver != null) { queue.add(() -> { try { - return (T) beanFactory.apply(type); + return resolver.getService(type); } catch (Exception e) { return null; } diff --git a/sample/pom.xml b/sample/pom.xml index 7d9701b..7ca1c3d 100644 --- a/sample/pom.xml +++ b/sample/pom.xml @@ -35,6 +35,21 @@ osba 1.0.0 + + com.euonia + pipeline + 1.0.0 + + + com.euonia + domain-driven-design + 1.0.0 + + + com.euonia + spring + 1.0.0 + org.springframework.boot spring-boot-starter-data-jpa @@ -54,6 +69,11 @@ mysql-connector-j runtime + + com.h2database + h2 + runtime + org.projectlombok lombok diff --git a/sample/src/main/java/com/euonia/sample/SampleApplication.java b/sample/src/main/java/com/euonia/sample/SampleApplication.java index 6f3ee2c..5ed9a0c 100644 --- a/sample/src/main/java/com/euonia/sample/SampleApplication.java +++ b/sample/src/main/java/com/euonia/sample/SampleApplication.java @@ -1,9 +1,12 @@ package com.euonia.sample; +//import com.euonia.pipeline.spring.PipelineConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; @SpringBootApplication +//@Import(PipelineConfiguration.class) public class SampleApplication { public static void main(String[] args) { diff --git a/sample/src/main/java/com/euonia/sample/controller/PipelineDemoController.java b/sample/src/main/java/com/euonia/sample/controller/PipelineDemoController.java new file mode 100644 index 0000000..b1b2203 --- /dev/null +++ b/sample/src/main/java/com/euonia/sample/controller/PipelineDemoController.java @@ -0,0 +1,69 @@ +package com.euonia.sample.controller; + +import com.euonia.pipeline.PipelineFactory; +import com.euonia.pipeline.RequestResponsePipelineBehavior; +import com.euonia.pipeline.RequestResponsePipelineDelegate; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import com.euonia.pipeline.PipelineBehaviors; + +@RestController +@RequestMapping("/api/pipeline") +public class PipelineDemoController { + private final PipelineFactory pipelineFactory; + + public PipelineDemoController(PipelineFactory pipelineFactory) { + this.pipelineFactory = pipelineFactory; + } + + @GetMapping("/echo") + public ResponseEntity> echo(@RequestParam(defaultValue = "hello") String value) { + EchoRequest request = new EchoRequest(value); + + var pipeline = pipelineFactory.createRequestResponse(); + + pipeline.use(EchoSuffixBehavior.class); + + String result = pipeline.runAsync(request, r -> CompletableFuture.completedFuture(r.value())) + .toCompletableFuture() + .join(); + + Map response = new LinkedHashMap<>(); + response.put("input", value); + response.put("output", result); + return ResponseEntity.ok(response); + } + + @PipelineBehaviors({EchoUpperCaseBehavior.class}) + private record EchoRequest(String value) { + } + + private static class EchoUpperCaseBehavior implements RequestResponsePipelineBehavior { + @Override + public CompletionStage handleAsync(EchoRequest context, RequestResponsePipelineDelegate next) { + return next.invoke(new EchoRequest(context.value().toUpperCase())); + } + } + + private static class EchoSuffixBehavior { + private final RequestResponsePipelineDelegate next; + + private EchoSuffixBehavior(RequestResponsePipelineDelegate next) { + this.next = next; + } + + public CompletionStage handleAsync(EchoRequest context) { + return next.invoke(context).thenApply(value -> value + "-pipeline"); + } + } +} diff --git a/sample/src/main/java/com/euonia/sample/controller/UserController.java b/sample/src/main/java/com/euonia/sample/controller/UserController.java new file mode 100644 index 0000000..6c973f7 --- /dev/null +++ b/sample/src/main/java/com/euonia/sample/controller/UserController.java @@ -0,0 +1,33 @@ +package com.euonia.sample.controller; + +import com.euonia.factory.ObjectFactory; +import com.euonia.sample.domain.aggregate.User; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.annotation.RequestScope; + +import java.util.Map; + +@RestController +@RequestMapping("/api/user") +@RequestScope +public class UserController { + private final ObjectFactory factory; + + public UserController(ObjectFactory factory) { + this.factory = factory; + } + + @PostMapping() + public ResponseEntity createUser(@RequestBody Map params) { + var user = factory.create(User.class, params.getOrDefault("name", "Default Name")); + user.save(false); + return ResponseEntity.ok("User created with name: " + user.getName()); + } + + @GetMapping("{id}") + public ResponseEntity getUser(@PathVariable long id) { + var user = factory.fetch(User.class, id); + return ResponseEntity.ok("User fetched with name: " + user.getName()); + } +} diff --git a/sample/src/main/java/com/euonia/sample/domain/EditableObjectBase.java b/sample/src/main/java/com/euonia/sample/domain/EditableObjectBase.java new file mode 100644 index 0000000..ba68eb0 --- /dev/null +++ b/sample/src/main/java/com/euonia/sample/domain/EditableObjectBase.java @@ -0,0 +1,57 @@ +package com.euonia.sample.domain; + +import com.euonia.domain.Aggregate; +import com.euonia.domain.HasDomainEvents; +import com.euonia.domain.event.DomainEvent; +import com.euonia.osba.EditableObject; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; + +@Component +@Scope("prototype") +public abstract class EditableObjectBase, ID extends Comparable> extends EditableObject implements Aggregate, HasDomainEvents { + private final List events = new ArrayList<>(); + private final ConcurrentMap, List>> eventHandlers = new ConcurrentHashMap<>(); + + public void registerEvent(Class eventType, Consumer handler) { + eventHandlers.computeIfAbsent(eventType, k -> new ArrayList<>()) + .add(event -> handler.accept(eventType.cast(event))); + } + + @Override + public List getEvents() { + return List.copyOf(events); + } + + @Override + public void raiseEvent(E event) { + applyEvent(event); + events.add(event); + } + + @Override + public void applyEvent(E event) { + var handlers = eventHandlers.getOrDefault(event.getClass(), null); + if (handlers != null) { + handlers.forEach(handler -> handler.accept(event)); + } + } + + @Override + public void clearEvents() { + events.clear(); + } + + @Override + public void attachEvents() { + for (DomainEvent event : events) { + event.attach(this); + } + } +} diff --git a/sample/src/main/java/com/euonia/sample/domain/aggregate/User.java b/sample/src/main/java/com/euonia/sample/domain/aggregate/User.java index 00e74f2..92bd49c 100644 --- a/sample/src/main/java/com/euonia/sample/domain/aggregate/User.java +++ b/sample/src/main/java/com/euonia/sample/domain/aggregate/User.java @@ -1,16 +1,107 @@ package com.euonia.sample.domain.aggregate; -import com.euonia.osba.EditableObject; +import com.euonia.annotation.Required; +import com.euonia.core.ObjectId; +import com.euonia.factory.annotation.FactoryCreate; +import com.euonia.osba.rules.LambdaRule; +import com.euonia.osba.rules.RuleBase; +import com.euonia.osba.rules.RuleContext; +import com.euonia.reflection.DisplayName; +import com.euonia.reflection.PropertyInfo; +import com.euonia.sample.domain.EditableObjectBase; +import com.euonia.sample.domain.event.UserCreatedEvent; +import com.euonia.sample.persistent.UserRepository; -public class User extends EditableObject { +import java.util.Objects; +import java.util.concurrent.CompletableFuture; - private String name; +public class User extends EditableObjectBase { + + private final PropertyInfo id = registerProperty(Long.class, "id"); + @DisplayName("User Name") + @Required(message = "name is valid") + private final PropertyInfo name = registerProperty(String.class, "name"); + + @DisplayName("User age") + private final PropertyInfo age = registerProperty(Integer.class, "age"); + + @Override + public Long getId() { + return getProperty(id); + } + + @Override + public void setId(Long id) { + loadProperty(this.id, id); + } public String getName() { - return name; + return getProperty(this.name); } public void setName(String name) { - this.name = name; + setProperty(this.name, name); + } + + public int getAge() { + return getProperty(age); + } + + public void setAge(int age) { + setProperty(this.age, age); + } + + @Override + protected void addRules() { + super.addRules(); + getRules().addRule(new UserNameRule(this.name)); + getRules().addRule(new LambdaRule<>(this.age, (age, context) -> age != null && age >= 18, "Age must be at least 18")); + } + + @FactoryCreate + protected void create(String name) { + super.create(); + setName(name); + setId(Objects.requireNonNull(ObjectId.snowflake().getValue(Long.class))); + raiseEvent(new UserCreatedEvent(getId(), name)); + } + + @Override + protected void insert() { + super.insert(); + var repository = getBusinessContext().getOrCreateObject(UserRepository.class); + } + + protected void fetch(long id) { + try (var x = bypassRuleChecks()) { + setName("Test User"); + } catch (Exception ex) { + // + } + } + + public class UserNameRule extends RuleBase { + + public UserNameRule(PropertyInfo property) { + super(property); + } + + @Override + public CompletableFuture executeAsync(RuleContext context) { + return CompletableFuture.runAsync(() -> { + + if (!(context.getTarget() instanceof User user)) { + return; + } + + var name = user.getName(); + + if (name == null || name.trim().isEmpty()) { + context.addErrorResult(String.format("%s cannot be empty", getProperty().getFriendlyName())); + } else if (name.length() < 12) { + context.addErrorResult(String.format("%s must be 12 characters", getProperty().getFriendlyName())); + } + }); + } } } diff --git a/sample/src/main/java/com/euonia/sample/domain/event/UserCreatedEvent.java b/sample/src/main/java/com/euonia/sample/domain/event/UserCreatedEvent.java new file mode 100644 index 0000000..6691654 --- /dev/null +++ b/sample/src/main/java/com/euonia/sample/domain/event/UserCreatedEvent.java @@ -0,0 +1,13 @@ +package com.euonia.sample.domain.event; + +import com.euonia.domain.event.DomainEventBase; + +public class UserCreatedEvent extends DomainEventBase { + private final long id; + private final String name; + + public UserCreatedEvent(long id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/sample/src/main/java/com/euonia/sample/persistent/UserRepository.java b/sample/src/main/java/com/euonia/sample/persistent/UserRepository.java new file mode 100644 index 0000000..03e7250 --- /dev/null +++ b/sample/src/main/java/com/euonia/sample/persistent/UserRepository.java @@ -0,0 +1,4 @@ +package com.euonia.sample.persistent; + +public interface UserRepository { +} diff --git a/sample/src/main/resources/application.yaml b/sample/src/main/resources/application.yaml index 6061b5a..38d0772 100644 --- a/sample/src/main/resources/application.yaml +++ b/sample/src/main/resources/application.yaml @@ -1,3 +1,18 @@ spring: application: name: sample + datasource: + url: jdbc:h2:mem:linkyoudb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + driver-class-name: org.h2.Driver + username: sa + password: + jpa: + hibernate: + ddl-auto: update + show-sql: true + web: + error: + include-exception: true + include-message: always + include-binding-errors: always + include-stacktrace: on-param diff --git a/spring/pom.xml b/spring/pom.xml index 0489775..8094708 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -19,6 +19,12 @@ ${revision} + + com.euonia + osba + ${revision} + + com.euonia unit-of-work diff --git a/spring/src/main/java/com/euonia/osba/OsbaConfiguration.java b/spring/src/main/java/com/euonia/osba/OsbaConfiguration.java new file mode 100644 index 0000000..22658c8 --- /dev/null +++ b/spring/src/main/java/com/euonia/osba/OsbaConfiguration.java @@ -0,0 +1,17 @@ +package com.euonia.osba; + +import com.euonia.factory.BusinessObjectFactory; +import com.euonia.factory.ObjectFactory; +import com.euonia.reflection.ServiceResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OsbaConfiguration { + @Bean + public ObjectFactory objectFactory(ServiceResolver resolver) { + var factory = new BusinessObjectFactory(); + factory.use(resolver); + return factory; + } +} diff --git a/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index f144323..b48ac21 100644 --- a/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ com.euonia.uow.UnitOfWorkAutoConfiguration com.euonia.reflection.ServiceResolverConfiguration +com.euonia.osba.OsbaConfiguration diff --git a/uow/pom.xml b/uow/pom.xml index 178f1f1..6fa3064 100644 --- a/uow/pom.xml +++ b/uow/pom.xml @@ -23,7 +23,7 @@ org.junit.jupiter junit-jupiter - 5.12.2 + ${junit.jupiter.version} test @@ -33,7 +33,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + ${maven.surefire.plugin.version}