Polish account context
This commit is contained in:
@@ -26,17 +26,17 @@ public class Account extends BaseEntity {
|
||||
private String accountNumber;
|
||||
private Boolean defaultAccount;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@OneToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
|
||||
private Set<AccountEvent> events = new HashSet<>();
|
||||
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
private AccountEventStatus status;
|
||||
private AccountStatus status;
|
||||
|
||||
public Account() {
|
||||
status = AccountEventStatus.ACCOUNT_CREATED;
|
||||
status = AccountStatus.ACCOUNT_CREATED;
|
||||
}
|
||||
|
||||
public Account(String accountNumber, Boolean defaultAccount, AccountEventStatus status) {
|
||||
public Account(String accountNumber, Boolean defaultAccount, AccountStatus status) {
|
||||
this.accountNumber = accountNumber;
|
||||
this.defaultAccount = defaultAccount;
|
||||
this.status = status;
|
||||
@@ -83,11 +83,11 @@ public class Account extends BaseEntity {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
public AccountEventStatus getStatus() {
|
||||
public AccountStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(AccountEventStatus status) {
|
||||
public void setStatus(AccountStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package demo.account;
|
||||
/**
|
||||
* The {@link AccountCommand} represents an action that can be performed to an
|
||||
* {@link Account} aggregate. Commands initiate an action that can mutate the state of
|
||||
* an account entity as it transitions between {@link AccountEventStatus} values.
|
||||
* an account entity as it transitions between {@link AccountStatus} values.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package demo.account;
|
||||
|
||||
import demo.event.AccountEvent;
|
||||
import demo.event.EventService;
|
||||
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
|
||||
import demo.event.*;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -12,7 +10,7 @@ import org.springframework.util.Assert;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import static demo.account.AccountEventStatus.*;
|
||||
import static demo.account.AccountStatus.*;
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
/**
|
||||
@@ -30,13 +28,10 @@ public class AccountService {
|
||||
|
||||
private final AccountRepository accountRepository;
|
||||
private final EventService eventService;
|
||||
private final RepositoryEntityLinks entityLinks;
|
||||
|
||||
public AccountService(AccountRepository accountRepository, EventService eventService,
|
||||
RepositoryEntityLinks entityLinks) {
|
||||
public AccountService(AccountRepository accountRepository, EventService eventService) {
|
||||
this.accountRepository = accountRepository;
|
||||
this.eventService = eventService;
|
||||
this.entityLinks = entityLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,10 +98,9 @@ public class AccountService {
|
||||
|
||||
if (event != null) {
|
||||
eventResource = new Resource<>(event,
|
||||
linkTo(AccountController.class)
|
||||
.slash("accounts")
|
||||
.slash(accountId)
|
||||
linkTo(EventController.class)
|
||||
.slash("events")
|
||||
.slash(event.getEventId())
|
||||
.withSelfRel(),
|
||||
linkTo(AccountController.class)
|
||||
.slash("accounts")
|
||||
@@ -130,11 +124,11 @@ public class AccountService {
|
||||
|
||||
Assert.notNull(account, "The account for the supplied id could not be found");
|
||||
|
||||
AccountEventStatus status = account.getContent().getStatus();
|
||||
AccountStatus status = account.getContent().getStatus();
|
||||
|
||||
switch (accountCommand) {
|
||||
case CONFIRM_ACCOUNT:
|
||||
Assert.isTrue(status == ACCOUNT_CREATED, "The account has already been confirmed");
|
||||
Assert.isTrue(status == ACCOUNT_PENDING, "The account has already been confirmed");
|
||||
|
||||
// Confirm the account
|
||||
Account updateAccount = account.getContent();
|
||||
@@ -302,10 +296,23 @@ public class AccountService {
|
||||
* @return the updated {@link Account} entity
|
||||
*/
|
||||
private Account updateAccount(Long id, Account account) {
|
||||
Assert.notNull(id);
|
||||
Assert.notNull(account);
|
||||
Assert.isTrue(Objects.equals(id, account.getAccountId()));
|
||||
return accountRepository.save(account);
|
||||
Assert.notNull(id, "Account id must be present in the resource URL");
|
||||
Assert.notNull(account, "Account request body cannot be null");
|
||||
|
||||
if(account.getAccountId() != null) {
|
||||
Assert.isTrue(Objects.equals(id, account.getAccountId()),
|
||||
"The account id in the request body must match the resource URL");
|
||||
} else {
|
||||
account.setAccountId(id);
|
||||
}
|
||||
|
||||
Account currentAccount = getAccount(id);
|
||||
currentAccount.setStatus(account.getStatus());
|
||||
currentAccount.setDefaultAccount(account.getDefaultAccount());
|
||||
currentAccount.setAccountNumber(account.getAccountNumber());
|
||||
currentAccount.setUserId(account.getUserId());
|
||||
|
||||
return accountRepository.save(currentAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package demo.account;
|
||||
|
||||
/**
|
||||
* The {@link AccountEventStatus} describes the state of an {@link Account}.
|
||||
* The {@link AccountStatus} describes the state of an {@link Account}.
|
||||
* The aggregate state of a {@link Account} is sourced from attached domain
|
||||
* events in the form of {@link demo.event.AccountEvent}.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
public enum AccountEventStatus {
|
||||
public enum AccountStatus {
|
||||
ACCOUNT_CREATED,
|
||||
ACCOUNT_PENDING,
|
||||
ACCOUNT_CONFIRMED,
|
||||
ACCOUNT_ACTIVE,
|
||||
ACCOUNT_SUSPENDED,
|
||||
@@ -2,7 +2,6 @@ package demo.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.domain.BaseEntity;
|
||||
import demo.log.Log;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
@@ -33,11 +32,11 @@ public class AccountEvent extends BaseEntity {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private AccountEventType type;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@OneToOne(cascade = CascadeType.MERGE,fetch = FetchType.LAZY)
|
||||
@JsonIgnore
|
||||
private Account account;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@OneToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
|
||||
@JsonIgnore
|
||||
private Set<Log> logs = new HashSet<>();
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package demo.account;
|
||||
package demo.event;
|
||||
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountStatus;
|
||||
|
||||
/**
|
||||
* The {@link AccountEventType} represents a collection of possible events that describe
|
||||
* state transitions of {@link AccountEventStatus} on the {@link Account} aggregate.
|
||||
* state transitions of {@link AccountStatus} on the {@link Account} aggregate.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
@@ -1,19 +1,27 @@
|
||||
package demo.account;
|
||||
package demo.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import demo.event.AccountEvent;
|
||||
import demo.event.EventController;
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountController;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
/**
|
||||
* The {@link AccountEvents} is a hypermedia collection of {@link AccountEvent} resources.
|
||||
*
|
||||
* @author kbastani
|
||||
*/
|
||||
public class AccountEvents extends Resources<AccountEvent> {
|
||||
|
||||
private Long accountId;
|
||||
|
||||
/**
|
||||
* Creates an empty {@link Resources} instance.
|
||||
* Create a new {@link AccountEvents} hypermedia resources collection for an {@link Account}.
|
||||
*
|
||||
* @param accountId is the unique identifier for the {@link Account}
|
||||
* @param content is the collection of {@link AccountEvents} attached to the {@link Account}
|
||||
*/
|
||||
public AccountEvents(Long accountId, Iterable<AccountEvent> content) {
|
||||
this(content);
|
||||
@@ -45,10 +53,15 @@ public class AccountEvents extends Resources<AccountEvent> {
|
||||
* @param content must not be {@literal null}.
|
||||
* @param links the links to be added to the {@link Resources}.
|
||||
*/
|
||||
public AccountEvents(Iterable<AccountEvent> content, Link... links) {
|
||||
private AccountEvents(Iterable<AccountEvent> content, Link... links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Account} identifier that the {@link AccountEvents} apply to.
|
||||
*
|
||||
* @return the account identifier
|
||||
*/
|
||||
@JsonIgnore
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
@@ -2,8 +2,6 @@ package demo.event;
|
||||
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountController;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountEvents;
|
||||
import demo.log.Log;
|
||||
import demo.log.LogRepository;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
@@ -118,6 +116,17 @@ public class EventService {
|
||||
return logResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link AccountEvents} for the supplied {@link Account} identifier.
|
||||
*
|
||||
* @param id is the unique identifier of the {@link Account}
|
||||
* @return a list of {@link AccountEvent} wrapped in a hypermedia {@link AccountEvents} resource
|
||||
*/
|
||||
public AccountEvents getEvents(Long id) {
|
||||
Page<AccountEvent> events = eventRepository.findAccountEventsByAccountId(id, new PageRequest(0, Integer.MAX_VALUE));
|
||||
return new AccountEvents(id, events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hypermedia resource for a {@link Log} entity.
|
||||
*
|
||||
@@ -148,10 +157,6 @@ public class EventService {
|
||||
.slash("events")
|
||||
.slash(event.getEventId())
|
||||
.withSelfRel(),
|
||||
entityLinks.linkFor(AccountEvent.class)
|
||||
.slash(event.getId())
|
||||
.slash("logs")
|
||||
.withRel("logs"),
|
||||
linkTo(AccountController.class)
|
||||
.slash("accounts")
|
||||
.slash(event.getAccount().getAccountId())
|
||||
@@ -169,15 +174,4 @@ public class EventService {
|
||||
event = eventRepository.save(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link AccountEvents} for the supplied {@link Account} identifier.
|
||||
*
|
||||
* @param id is the unique identifier of the {@link Account}
|
||||
* @return a list of {@link AccountEvent} wrapped in a hypermedia {@link AccountEvents} resource
|
||||
*/
|
||||
public AccountEvents getEvents(Long id) {
|
||||
Page<AccountEvent> events = eventRepository.findAccountEventsByAccountId(id, new PageRequest(0, Integer.MAX_VALUE));
|
||||
return new AccountEvents(id, events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package demo.log;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.event.AccountEventType;
|
||||
import demo.domain.BaseEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
server.port=0
|
||||
spring.cloud.stream.bindings.output.destination=account
|
||||
spring.cloud.stream.bindings.output.contentType=application/json
|
||||
@@ -0,0 +1,12 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: development
|
||||
---
|
||||
spring:
|
||||
profiles: development
|
||||
cloud:
|
||||
stream:
|
||||
bindings:
|
||||
output:
|
||||
destination: account
|
||||
contentType: 'application/json'
|
||||
@@ -0,0 +1,5 @@
|
||||
spring:
|
||||
application:
|
||||
name: account-web
|
||||
server:
|
||||
port: 0
|
||||
@@ -23,6 +23,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@ public class Account extends BaseEntity {
|
||||
private Long userId;
|
||||
private String accountNumber;
|
||||
private Boolean defaultAccount;
|
||||
private AccountEventStatus status;
|
||||
private AccountStatus status;
|
||||
|
||||
public Account() {
|
||||
}
|
||||
@@ -35,11 +35,11 @@ public class Account extends BaseEntity {
|
||||
this.defaultAccount = defaultAccount;
|
||||
}
|
||||
|
||||
public AccountEventStatus getStatus() {
|
||||
public AccountStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(AccountEventStatus status) {
|
||||
public void setStatus(AccountStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
package demo.account;
|
||||
|
||||
/**
|
||||
* The {@link AccountEventStatus} describes the state of an {@link Account}.
|
||||
* The {@link AccountStatus} describes the state of an {@link Account}.
|
||||
* The aggregate state of a {@link Account} is sourced from attached domain
|
||||
* events in the form of {@link demo.event.AccountEvent}.
|
||||
*/
|
||||
public enum AccountEventStatus {
|
||||
public enum AccountStatus {
|
||||
ACCOUNT_CREATED,
|
||||
ACCOUNT_PENDING,
|
||||
ACCOUNT_CONFIRMED,
|
||||
ACCOUNT_ACTIVE,
|
||||
ACCOUNT_SUSPENDED,
|
||||
ACCOUNT_ARCHIVED;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
|
||||
ACCOUNT_ARCHIVED
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import demo.event.AccountEvent;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
@@ -10,9 +10,9 @@ public abstract class AccountCommand {
|
||||
|
||||
final private Logger log = Logger.getLogger(AccountCommand.class);
|
||||
|
||||
final private StateContext<AccountEventStatus, AccountEventType> context;
|
||||
final private StateContext<AccountStatus, AccountEventType> context;
|
||||
|
||||
public AccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public AccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class ActivateAccountCommand extends AccountCommand {
|
||||
public ActivateAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public ActivateAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class ArchiveAccountCommand extends AccountCommand {
|
||||
public ArchiveAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public ArchiveAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import demo.event.AccountEvent;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
@@ -15,10 +15,15 @@ public class ConfirmAccountCommand extends AccountCommand {
|
||||
|
||||
final private Logger log = Logger.getLogger(ConfirmAccountCommand.class);
|
||||
|
||||
public ConfirmAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public ConfirmAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@link AccountEvent} to the {@link Account} aggregate.
|
||||
*
|
||||
* @param event is the {@link AccountEvent} for this context
|
||||
*/
|
||||
@Override
|
||||
public void apply(AccountEvent event) throws Exception {
|
||||
super.apply(event);
|
||||
|
||||
@@ -1,41 +1,84 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEvent;
|
||||
import demo.event.AccountEventType;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.client.Traverson;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class CreateAccountCommand extends AccountCommand {
|
||||
|
||||
final private Logger log = Logger.getLogger(CreateAccountCommand.class);
|
||||
|
||||
public CreateAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public CreateAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@link AccountEvent} to the {@link Account} aggregate.
|
||||
*
|
||||
* @param event is the {@link AccountEvent} for this context
|
||||
*/
|
||||
@Override
|
||||
public void apply(AccountEvent event) throws Exception {
|
||||
super.apply(event);
|
||||
|
||||
// Create a new hypermedia traversal for the account
|
||||
// Create a traverson for the root account
|
||||
Traverson traverson = new Traverson(
|
||||
new URI(event.getLink("account").getHref()),
|
||||
MediaTypes.HAL_JSON
|
||||
);
|
||||
|
||||
|
||||
// Traverse to confirm the account
|
||||
Account account = traverson.follow("commands")
|
||||
.follow("confirm")
|
||||
// Get the account resource attached to the event
|
||||
Account account = traverson.follow("self")
|
||||
.toEntity(Account.class)
|
||||
.getBody();
|
||||
|
||||
log.info("Account confirmed: " + account);
|
||||
// Set the account to a pending state
|
||||
account = setAccountPendingStatus(event, account);
|
||||
|
||||
// The account can only be confirmed if it is in a pending state
|
||||
if (account.getStatus() == AccountStatus.ACCOUNT_PENDING) {
|
||||
// Traverse to the confirm account command
|
||||
account = traverson.follow("commands")
|
||||
.follow("confirm")
|
||||
.toEntity(Account.class)
|
||||
.getBody();
|
||||
|
||||
log.info("Account confirmed: " + account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Account} resource to a pending state.
|
||||
*
|
||||
* @param event is the {@link AccountEvent} for this context
|
||||
* @param account is the {@link Account} attached to the {@link AccountEvent} resource
|
||||
* @return an {@link Account} with its updated state set to pending
|
||||
* @throws URISyntaxException is thrown if the {@link Account} hypermedia link cannot be parsed
|
||||
*/
|
||||
private Account setAccountPendingStatus(AccountEvent event, Account account) throws URISyntaxException {
|
||||
// Set the account status to pending
|
||||
account.setStatus(AccountStatus.ACCOUNT_PENDING);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
// Create a new request entity
|
||||
RequestEntity<Account> requestEntity = RequestEntity.put(
|
||||
new URI(event.getLink("account").getHref()))
|
||||
.contentType(MediaTypes.HAL_JSON)
|
||||
.body(account);
|
||||
|
||||
// Update the account entity's status
|
||||
account = restTemplate.exchange(requestEntity, Account.class).getBody();
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class SuspendAccountCommand extends AccountCommand {
|
||||
public SuspendAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public SuspendAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class UnarchiveAccountCommand extends AccountCommand {
|
||||
public UnarchiveAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public UnarchiveAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package demo.command;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
|
||||
public class UnsuspendAccountCommand extends AccountCommand {
|
||||
public UnsuspendAccountCommand(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
public UnsuspendAccountCommand(StateContext<AccountStatus, AccountEventType> context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package demo.event;
|
||||
|
||||
import demo.account.AccountEventType;
|
||||
import demo.domain.BaseEntity;
|
||||
|
||||
public class AccountEvent extends BaseEntity {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package demo.event;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountEvents;
|
||||
import demo.account.Account;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.state.StateMachineService;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -22,7 +21,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link AccountEventStream} monitors for a variety of {@link AccountEvent} domain
|
||||
* events for an {@link demo.account.Account}.
|
||||
* events for an {@link Account}.
|
||||
*/
|
||||
@EnableAutoConfiguration
|
||||
@EnableBinding(Sink.class)
|
||||
@@ -46,7 +45,7 @@ public class AccountEventStream {
|
||||
logger.info("Account event received: " + accountEvent.toString());
|
||||
|
||||
// Create a new ephemeral account state machine
|
||||
StateMachine<AccountEventStatus, AccountEventType> stateMachine =
|
||||
StateMachine<AccountStatus, AccountEventType> stateMachine =
|
||||
stateMachineService.getStateMachine();
|
||||
|
||||
// Traverse the hypermedia link for the attached account
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package demo.account;
|
||||
package demo.event;
|
||||
|
||||
public enum AccountEventType {
|
||||
ACCOUNT_CREATED,
|
||||
@@ -1,6 +1,5 @@
|
||||
package demo.account;
|
||||
package demo.event;
|
||||
|
||||
import demo.event.AccountEvent;
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
public class AccountEvents extends Resources<AccountEvent> {
|
||||
@@ -1,7 +1,7 @@
|
||||
package demo.state;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import demo.command.*;
|
||||
import demo.event.AccountEvent;
|
||||
import org.apache.log4j.Logger;
|
||||
@@ -18,69 +18,69 @@ import java.util.EnumSet;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<AccountEventStatus, AccountEventType> {
|
||||
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<AccountStatus, AccountEventType> {
|
||||
|
||||
final private Logger log = Logger.getLogger(StateMachineConfig.class);
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<AccountEventStatus, AccountEventType> states)
|
||||
public void configure(StateMachineStateConfigurer<AccountStatus, AccountEventType> states)
|
||||
throws Exception {
|
||||
// Describe initial condition of account status
|
||||
states.withStates()
|
||||
.initial(AccountEventStatus.ACCOUNT_CREATED)
|
||||
.states(EnumSet.allOf(AccountEventStatus.class));
|
||||
.initial(AccountStatus.ACCOUNT_CREATED)
|
||||
.states(EnumSet.allOf(AccountStatus.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<AccountEventStatus, AccountEventType> transitions)
|
||||
public void configure(StateMachineTransitionConfigurer<AccountStatus, AccountEventType> transitions)
|
||||
throws Exception {
|
||||
// Describe state machine transitions for accounts
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_CREATED)
|
||||
.target(AccountEventStatus.ACCOUNT_PENDING)
|
||||
.source(AccountStatus.ACCOUNT_CREATED)
|
||||
.target(AccountStatus.ACCOUNT_PENDING)
|
||||
.event(AccountEventType.ACCOUNT_CREATED)
|
||||
.action(createAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_PENDING)
|
||||
.target(AccountEventStatus.ACCOUNT_CONFIRMED)
|
||||
.source(AccountStatus.ACCOUNT_PENDING)
|
||||
.target(AccountStatus.ACCOUNT_CONFIRMED)
|
||||
.event(AccountEventType.ACCOUNT_CONFIRMED)
|
||||
.action(confirmAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_CONFIRMED)
|
||||
.target(AccountEventStatus.ACCOUNT_ACTIVE)
|
||||
.source(AccountStatus.ACCOUNT_CONFIRMED)
|
||||
.target(AccountStatus.ACCOUNT_ACTIVE)
|
||||
.event(AccountEventType.ACCOUNT_ACTIVATED)
|
||||
.action(activateAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_ACTIVE)
|
||||
.target(AccountEventStatus.ACCOUNT_ARCHIVED)
|
||||
.source(AccountStatus.ACCOUNT_ACTIVE)
|
||||
.target(AccountStatus.ACCOUNT_ARCHIVED)
|
||||
.event(AccountEventType.ACCOUNT_ARCHIVED)
|
||||
.action(archiveAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_ACTIVE)
|
||||
.target(AccountEventStatus.ACCOUNT_SUSPENDED)
|
||||
.source(AccountStatus.ACCOUNT_ACTIVE)
|
||||
.target(AccountStatus.ACCOUNT_SUSPENDED)
|
||||
.event(AccountEventType.ACCOUNT_SUSPENDED)
|
||||
.action(suspendAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_ARCHIVED)
|
||||
.target(AccountEventStatus.ACCOUNT_ACTIVE)
|
||||
.source(AccountStatus.ACCOUNT_ARCHIVED)
|
||||
.target(AccountStatus.ACCOUNT_ACTIVE)
|
||||
.event(AccountEventType.ACCOUNT_ACTIVATED)
|
||||
.action(unarchiveAccount())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(AccountEventStatus.ACCOUNT_SUSPENDED)
|
||||
.target(AccountEventStatus.ACCOUNT_ACTIVE)
|
||||
.source(AccountStatus.ACCOUNT_SUSPENDED)
|
||||
.target(AccountStatus.ACCOUNT_ACTIVE)
|
||||
.event(AccountEventType.ACCOUNT_ACTIVATED)
|
||||
.action(unsuspendAccount());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> createAccount() {
|
||||
public Action<AccountStatus, AccountEventType> createAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -95,7 +95,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> confirmAccount() {
|
||||
public Action<AccountStatus, AccountEventType> confirmAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -110,7 +110,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> activateAccount() {
|
||||
public Action<AccountStatus, AccountEventType> activateAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -125,7 +125,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> archiveAccount() {
|
||||
public Action<AccountStatus, AccountEventType> archiveAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -140,7 +140,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> suspendAccount() {
|
||||
public Action<AccountStatus, AccountEventType> suspendAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -155,7 +155,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> unarchiveAccount() {
|
||||
public Action<AccountStatus, AccountEventType> unarchiveAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -170,7 +170,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<AccountEventStatus, AccountEventType> unsuspendAccount() {
|
||||
public Action<AccountStatus, AccountEventType> unsuspendAccount() {
|
||||
return context -> {
|
||||
AccountEvent accountEvent = replicateEvent(context);
|
||||
if (accountEvent != null) {
|
||||
@@ -191,7 +191,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<Accoun
|
||||
* @param context the state machine context that may include an {@link AccountEvent}
|
||||
* @return an {@link AccountEvent} only if this event has not yet been processed, otherwise returns null
|
||||
*/
|
||||
private AccountEvent replicateEvent(StateContext<AccountEventStatus, AccountEventType> context) {
|
||||
private AccountEvent replicateEvent(StateContext<AccountStatus, AccountEventType> context) {
|
||||
AccountEvent currentEvent = null;
|
||||
log.info(context.getMessage());
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package demo.state;
|
||||
|
||||
import demo.account.AccountEventStatus;
|
||||
import demo.account.AccountEventType;
|
||||
import demo.account.AccountStatus;
|
||||
import demo.event.AccountEventType;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -11,14 +11,14 @@ import java.util.UUID;
|
||||
@Service
|
||||
public class StateMachineService {
|
||||
|
||||
private final StateMachineFactory<AccountEventStatus, AccountEventType> factory;
|
||||
private final StateMachineFactory<AccountStatus, AccountEventType> factory;
|
||||
|
||||
public StateMachineService(StateMachineFactory<AccountEventStatus, AccountEventType> factory) {
|
||||
public StateMachineService(StateMachineFactory<AccountStatus, AccountEventType> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public StateMachine<AccountEventStatus, AccountEventType> getStateMachine() {
|
||||
StateMachine<AccountEventStatus, AccountEventType> stateMachine =
|
||||
public StateMachine<AccountStatus, AccountEventType> getStateMachine() {
|
||||
StateMachine<AccountStatus, AccountEventType> stateMachine =
|
||||
factory.getStateMachine(UUID.randomUUID().toString());
|
||||
stateMachine.start();
|
||||
return stateMachine;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
spring.cloud.stream.bindings.input.destination=account
|
||||
spring.cloud.stream.bindings.input.group=account-group
|
||||
spring.cloud.stream.bindings.input.consumer.durableSubscription=true
|
||||
spring.cloud.stream.bindings.input.contentType=application/json
|
||||
server.port=0
|
||||
@@ -0,0 +1,17 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: development
|
||||
---
|
||||
spring:
|
||||
profiles: development
|
||||
cloud:
|
||||
stream:
|
||||
bindings:
|
||||
input:
|
||||
destination: account
|
||||
group: account-group
|
||||
contentType: 'application/json'
|
||||
consumer:
|
||||
durableSubscription: true
|
||||
server:
|
||||
port: 0
|
||||
@@ -0,0 +1,4 @@
|
||||
spring:
|
||||
application:
|
||||
name: account-worker
|
||||
---
|
||||
Reference in New Issue
Block a user