Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.euonia.application;

public interface ApplicationService {
}
Original file line number Diff line number Diff line change
@@ -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> T getService(Class<T> type) {
return (T) serviceResolver.getService(type);
}

protected UserPrincipal getUser() {
return serviceResolver.getService(UserPrincipal.class);
}
}
8 changes: 8 additions & 0 deletions ddd/src/main/java/com/euonia/domain/Aggregate.java
Original file line number Diff line number Diff line change
@@ -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 <ID> the type of the identifier for the aggregate
*/
public interface Aggregate<ID extends Comparable<ID>> extends Entity<ID> {

}
44 changes: 37 additions & 7 deletions ddd/src/main/java/com/euonia/domain/AggregateBase.java
Original file line number Diff line number Diff line change
@@ -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<ID extends Comparable<ID>> extends EntityBase<ID> implements Aggregate<ID> {
private final List<DomainEventBase> 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 <ID> the type of the identifier for the aggregate, which must be comparable.
*/
public abstract class AggregateBase<ID extends Comparable<ID>> extends EntityBase<ID> implements Aggregate<ID>, HasDomainEvents {
private final List<DomainEvent> events = new ArrayList<>();
private final ConcurrentMap<Class<? extends DomainEvent>, List<Consumer<DomainEvent>>> eventHandlers = new ConcurrentHashMap<>();

public List<DomainEventBase> getEvents() {
public <E extends DomainEvent> void registerEvent(Class<E> eventType, Consumer<E> handler) {
eventHandlers.computeIfAbsent(eventType, k -> new ArrayList<>())
.add(event -> handler.accept(eventType.cast(event)));
}

@Override
public List<DomainEvent> getEvents() {
return List.copyOf(events);
}

protected <E extends DomainEventBase> void raiseEvent(E event) {
@Override
public <E extends DomainEvent> void raiseEvent(E event) {
applyEvent(event);
events.add(event);
}

protected void clearEvents() {
@Override
public <E extends DomainEvent> 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);
}
}
Expand Down
20 changes: 19 additions & 1 deletion ddd/src/main/java/com/euonia/domain/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<ID extends Comparable<ID>> {

/**
* 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()};
}
}
57 changes: 57 additions & 0 deletions ddd/src/main/java/com/euonia/domain/HasDomainEvents.java
Original file line number Diff line number Diff line change
@@ -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<DomainEvent> 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 <E> the type of the domain event
*/
<E extends DomainEvent> void registerEvent(Class<E> eventType, Consumer<E> handler);

/**
* Raises a domain event. The event will be processed by the registered handlers.
*
* @param event the domain event to raise
* @param <E> the type of the domain event
*/
<E extends DomainEvent> 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 <E> the type of the domain event
*/
<E extends DomainEvent> 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();
}
18 changes: 18 additions & 0 deletions ddd/src/main/java/com/euonia/usecase/UseCase.java
Original file line number Diff line number Diff line change
@@ -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 <I> the type of the input to the use case
* @param <O> the type of the output of the use case
*/
public interface UseCase<I, O> {
/**
* 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);
}
14 changes: 14 additions & 0 deletions ddd/src/main/java/com/euonia/usecase/UseCaseFailure.java
Original file line number Diff line number Diff line change
@@ -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);
}
72 changes: 72 additions & 0 deletions ddd/src/main/java/com/euonia/usecase/UseCasePresenter.java
Original file line number Diff line number Diff line change
@@ -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 <O> the type of the successful output of the use case
*/
public class UseCasePresenter<O> implements UseCaseSuccess<O>, UseCaseFailure, AutoCloseable {
private final Publisher<O> 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<O> onSuccess, Consumer<Throwable> 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<O>) publisher).submit(output);
}

@Override
public void error(Throwable throwable) {
((SubmissionPublisher<O>) publisher).closeExceptionally(throwable);
}

@Override
public void close() throws Exception {
((SubmissionPublisher<O>) 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;
}
}
16 changes: 16 additions & 0 deletions ddd/src/main/java/com/euonia/usecase/UseCaseSuccess.java
Original file line number Diff line number Diff line change
@@ -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 <O> the type of the successful output
*/
public interface UseCaseSuccess<O> {
/**
* Indicates that the use case execution has succeeded with a result.
*
* @param output the result of the successful execution
*/
void success(O output);
}
14 changes: 7 additions & 7 deletions osba/src/main/java/com/euonia/factory/BusinessObjectFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -19,16 +19,16 @@
@SuppressWarnings("UnusedReturnValue")
public class BusinessObjectFactory implements ObjectFactory {

private Function<Class<?>, ?> 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
*/
Comment on lines 24 to 29
public BusinessObjectFactory use(Function<Class<?>, ?> beanFactory) {
this.beanFactory = beanFactory;
public BusinessObjectFactory use(ServiceResolver resolver) {
this.resolver = resolver;
return this;
}

Expand Down Expand Up @@ -173,10 +173,10 @@ public <T> void delete(Class<T> type, Object... criteria) {
@SuppressWarnings("unchecked")
private <T> T getObjectInstance(Class<T> 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;
}
Expand Down
Loading