code links added to readme
This commit is contained in:
42
README.md
42
README.md
@@ -5,9 +5,10 @@ Not every piece of software is equally important...
|
|||||||
Not every piece will decide about company / product success or can cause not reversible
|
Not every piece will decide about company / product success or can cause not reversible
|
||||||
negative business consequences like materialise brand risk or money loses.
|
negative business consequences like materialise brand risk or money loses.
|
||||||
On the other hand scalability or non functional requirements are different for different activities in software.
|
On the other hand scalability or non functional requirements are different for different activities in software.
|
||||||
|
|
||||||
To accommodate to those differences, separate architectural patterns are applied:
|
To accommodate to those differences, separate architectural patterns are applied:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Simple Create Read Update Delete functionality** are exposed with leverage of CRUD framework.
|
**Simple Create Read Update Delete functionality** are exposed with leverage of CRUD framework.
|
||||||
|
|
||||||
@@ -16,6 +17,11 @@ Goals of that approach:
|
|||||||
- fast respond to typical changes (ex. „please add another 2 fields on UI”),
|
- fast respond to typical changes (ex. „please add another 2 fields on UI”),
|
||||||
- exposure of high quality API.
|
- exposure of high quality API.
|
||||||
|
|
||||||
|
Examples in code:
|
||||||
|
- CRUD-able document [ProductDescription](product-management-adapters/src/main/java/pl/com/bottega/factory/product/management/ProductDescription.java)
|
||||||
|
- persistence of document [ProductDescriptionEntity](product-management-adapters/src/main/java/pl/com/bottega/factory/product/management/ProductDescriptionEntity.java)
|
||||||
|
- CRUD exposed as DAO and REST endpoint [ProductDescriptionDao](product-management-adapters/src/main/java/pl/com/bottega/factory/product/management/ProductDescriptionDao.java)
|
||||||
|
|
||||||
**Complex Commands (business processing)** expressed in Domain Model which is embedded in hexagonal architecture.
|
**Complex Commands (business processing)** expressed in Domain Model which is embedded in hexagonal architecture.
|
||||||
|
|
||||||
Goals of that approach:
|
Goals of that approach:
|
||||||
@@ -25,6 +31,25 @@ caused by technological choices or transport models from external services / con
|
|||||||
- make the core business of application technology agnostic, enabling continues technology
|
- make the core business of application technology agnostic, enabling continues technology
|
||||||
migration and keeping long living projects up to date with fast evolving frameworks and libraries.
|
migration and keeping long living projects up to date with fast evolving frameworks and libraries.
|
||||||
|
|
||||||
|
Examples of Domain Model in code:
|
||||||
|
- aggregate [ProductDemand](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/ProductDemand.java)
|
||||||
|
- entity [DailyDemand](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/DailyDemand.java)
|
||||||
|
- value object [Adjustment](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/Adjustment.java)
|
||||||
|
- policy [ReviewPolicy](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/ReviewPolicy.java)
|
||||||
|
- domain event [DemandedLevelsChanged](shared-kernel-model/src/main/java/pl/com/bottega/factory/demand/forecasting/DemandedLevelsChanged.java)
|
||||||
|
|
||||||
|
Examples of Ports in code:
|
||||||
|
- application service (primary port) [DemandService](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/DemandService.java)
|
||||||
|
- repository (secondary port) [ProductDemandRepository](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/ProductDemandRepository.java)
|
||||||
|
- domain events handling (secondary port) [DemandEvents](demand-forecasting-model/src/main/java/pl/com/bottega/factory/demand/forecasting/DemandEvents.java)
|
||||||
|
|
||||||
|
Examples of Adapters in code:
|
||||||
|
- REST endpoint for complex command (driving adapter)
|
||||||
|
- command resource [DemandAdjustmentDao](demand-forecasting-adapters/src/main/java/pl/com/bottega/factory/demand/forecasting/command/DemandAdjustmentDao.java)
|
||||||
|
- command handler [CommandsHandler](demand-forecasting-adapters/src/main/java/pl/com/bottega/factory/demand/forecasting/command/CommandsHandler.java)
|
||||||
|
- repository implementation (driven adapter) [ProductDemandORMRepository](demand-forecasting-adapters/src/main/java/pl/com/bottega/factory/demand/forecasting/ProductDemandORMRepository.java)
|
||||||
|
- events propagation (driven adapter) [DemandEventsPropagation](app-monolith/src/main/java/pl/com/bottega/factory/demand/forecasting/DemandEventsPropagation.java)
|
||||||
|
|
||||||
**Complex Query** implemented as direct and simple as possible by:
|
**Complex Query** implemented as direct and simple as possible by:
|
||||||
- fetching persistent read model expected by consumer, the read model is a projection of past domain event,
|
- fetching persistent read model expected by consumer, the read model is a projection of past domain event,
|
||||||
- read model composed at query execution time build directly from persistent form of Domain Model,
|
- read model composed at query execution time build directly from persistent form of Domain Model,
|
||||||
@@ -34,15 +59,20 @@ Additional complex calculations or projections can be partially delegated to the
|
|||||||
|
|
||||||
Goals of that approach:
|
Goals of that approach:
|
||||||
- encapsulation of the Domain Model complexity by providing (simpler) consumer driven or published language API,
|
- encapsulation of the Domain Model complexity by providing (simpler) consumer driven or published language API,
|
||||||
- freeing Domain the Model from exposing data for reads making the Domain Model simpler,
|
- freeing the Domain Model from exposing data for reads making the Domain Model simpler,
|
||||||
- improves reads performance and enable horizontal scalability.
|
- improves reads performance and enable horizontal scalability.
|
||||||
|
|
||||||
|
Examples in code:
|
||||||
|
- projection of domain events to persistent read model [DeliveryForecastProjection](demand-forecasting-adapters/src/main/java/pl/com/bottega/factory/delivery/planning/projection/DeliveryForecastProjection.java)
|
||||||
|
- REST endpoint for persistent read model [DeliveryForecastDao](demand-forecasting-adapters/src/main/java/pl/com/bottega/factory/delivery/planning/projection/DeliveryForecastDao.java)
|
||||||
|
- read model composed at query execution time [StockForecastQuery](app-monolith/src/main/java/pl/com/bottega/factory/stock/forecast/StockForecastQuery.java)
|
||||||
|
- REST resource processor for NOT persistent read model [StockForecastResourceProcessor](app-monolith/src/main/java/pl/com/bottega/factory/stock/forecast/ressource/StockForecastResourceProcessor.java)
|
||||||
|
|
||||||
## Hexagonal Architecture
|
## Hexagonal Architecture
|
||||||
Only the most valuable part of that enterprise software is embedded in hexagonal architecture -
|
Only the most valuable part of that enterprise software is embedded in hexagonal architecture -
|
||||||
complex business processing modeled in form of the Domain Model.
|
complex business processing modeled in form of the Domain Model.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Application Services** - providing entry point to Domain Model functionality,
|
**Application Services** - providing entry point to Domain Model functionality,
|
||||||
Application Services are ports for Primary / Driving Adapters like RESTfull endpoints.
|
Application Services are ports for Primary / Driving Adapters like RESTfull endpoints.
|
||||||
@@ -69,13 +99,13 @@ with **Model Exploration Whirlpool** and build **Ubiquitous Language** with your
|
|||||||
Adding infrastructure and technology later is easy thanks to Hexagonal Architecture.
|
Adding infrastructure and technology later is easy thanks to Hexagonal Architecture.
|
||||||
|
|
||||||
Simply starting from ZERO business knowledge through initial domain and opportunity exploration with **Big Picture Event Storming**:
|
Simply starting from ZERO business knowledge through initial domain and opportunity exploration with **Big Picture Event Storming**:
|
||||||

|

|
||||||
|
|
||||||
after cleaning and trimming initial model to most valuable and needed areas:
|
after cleaning and trimming initial model to most valuable and needed areas:
|
||||||

|

|
||||||
|
|
||||||
Deep dive in **Demand Forecasting** sub-domain with **Design Level Event Storming**:
|
Deep dive in **Demand Forecasting** sub-domain with **Design Level Event Storming**:
|
||||||

|

|
||||||
|
|
||||||
is excellent canvas to cooperative exploration of:
|
is excellent canvas to cooperative exploration of:
|
||||||
- impacted and required actors,
|
- impacted and required actors,
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import pl.com.bottega.factory.delivery.planning.projection.DeliveryForecastProje
|
|||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RepositoryEventHandler
|
@RepositoryEventHandler
|
||||||
public class DeliveryPlannerDefinitionEventsMapping {
|
public class DeliveryPlannerDefinitionEventsPropagation {
|
||||||
|
|
||||||
private DeliveryForecastProjection projection;
|
private DeliveryForecastProjection projection;
|
||||||
|
|
||||||
@HandleAfterSave
|
@HandleAfterSave
|
||||||
@HandleAfterCreate
|
@HandleAfterCreate
|
||||||
public void handle(DeliveryPlannerDefinitionEntity entity) {
|
public void handleCreateAndUpdate(DeliveryPlannerDefinitionEntity entity) {
|
||||||
projection.handleDeliveryPlannerDefinitionChange(entity.getRefNo());
|
projection.applyDeliveryPlannerDefinitionChange(entity.getRefNo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ import java.util.stream.Collectors;
|
|||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
class DemandEventsMapping implements DemandEvents {
|
class DemandEventsPropagation implements DemandEvents {
|
||||||
|
|
||||||
private final CurrentDemandProjection demandProjection;
|
private final CurrentDemandProjection demandProjection;
|
||||||
private final DeliveryForecastProjection deliveryProjection;
|
private final DeliveryForecastProjection deliveryProjection;
|
||||||
@@ -26,8 +26,8 @@ class DemandEventsMapping implements DemandEvents {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(DemandedLevelsChanged event) {
|
public void emit(DemandedLevelsChanged event) {
|
||||||
demandProjection.persistCurrentDemands(event);
|
demandProjection.applyDemandedLevelsChanged(event);
|
||||||
deliveryProjection.persistDeliveryForecasts(event);
|
deliveryProjection.applyDemandedLevelsChanged(event);
|
||||||
shortagePrediction.predictShortages(event);
|
shortagePrediction.predictShortages(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@ package pl.com.bottega.factory.product.management;
|
|||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
|
import org.springframework.data.rest.core.annotation.HandleAfterCreate;
|
||||||
|
import org.springframework.data.rest.core.annotation.HandleAfterDelete;
|
||||||
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import pl.com.bottega.factory.demand.forecasting.DemandService;
|
import pl.com.bottega.factory.demand.forecasting.DemandService;
|
||||||
@@ -11,14 +12,19 @@ import pl.com.bottega.factory.stock.forecast.ressource.StockForecastEntity;
|
|||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RepositoryEventHandler
|
@RepositoryEventHandler
|
||||||
public class ProductDescriptionEventsMapping {
|
public class ProductDescriptionEventsPropagation {
|
||||||
|
|
||||||
private final DemandService demandService;
|
private final DemandService demandService;
|
||||||
private final StockForecastDao stockForecasts;
|
private final StockForecastDao stockForecasts;
|
||||||
|
|
||||||
@HandleAfterCreate
|
@HandleAfterCreate
|
||||||
public void handle(ProductDescriptionEntity entity) {
|
public void handleCreate(ProductDescriptionEntity entity) {
|
||||||
demandService.init(entity.getRefNo());
|
demandService.init(entity.getRefNo());
|
||||||
stockForecasts.save(new StockForecastEntity(entity.getRefNo()));
|
stockForecasts.save(new StockForecastEntity(entity.getRefNo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HandleAfterDelete
|
||||||
|
public void handleDelete(ProductDescriptionEntity entity) {
|
||||||
|
stockForecasts.delete(entity.getRefNo());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ import pl.com.bottega.factory.shortages.prediction.notification.NotificationOfSh
|
|||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
class ShortageEventsMapping implements ShortageEvents {
|
class ShortageEventsPropagation implements ShortageEvents {
|
||||||
|
|
||||||
private final NotificationOfShortage notification;
|
private final NotificationOfShortage notification;
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ public class StockForecastQuery {
|
|||||||
public StockForecast get(RefNoId refNo) {
|
public StockForecast get(RefNoId refNo) {
|
||||||
Stock stock = stocks.forRefNo(refNo);
|
Stock stock = stocks.forRefNo(refNo);
|
||||||
LocalDate today = LocalDate.now(clock);
|
LocalDate today = LocalDate.now(clock);
|
||||||
return build(refNo, today, stock,
|
return build(today, stock,
|
||||||
this.demands
|
this.demands
|
||||||
.findByRefNoAndDateGreaterThanEqual(refNo.getRefNo(), today).stream()
|
.findByRefNoAndDateGreaterThanEqual(refNo.getRefNo(), today).stream()
|
||||||
.collect(toMap(
|
.collect(toMap(
|
||||||
@@ -47,7 +47,7 @@ public class StockForecastQuery {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StockForecast build(RefNoId refNo, LocalDate today,
|
private StockForecast build(LocalDate today,
|
||||||
Stock stock,
|
Stock stock,
|
||||||
Map<LocalDate, Long> demands,
|
Map<LocalDate, Long> demands,
|
||||||
Map<LocalDate, Long> outputs) {
|
Map<LocalDate, Long> outputs) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package pl.com.bottega.factory.stock.forecast.ressource;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import pl.com.bottega.factory.product.management.RefNoId;
|
|
||||||
import pl.com.bottega.factory.stock.forecast.StockForecast;
|
|
||||||
import pl.com.bottega.factory.stock.forecast.StockForecastQuery;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class StaticAccess {
|
|
||||||
|
|
||||||
private static StockForecastQuery query;
|
|
||||||
|
|
||||||
StaticAccess(StockForecastQuery query) {
|
|
||||||
StaticAccess.query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StockForecast calculateQuery(String refNo) {
|
|
||||||
return query.get(new RefNoId(refNo));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package pl.com.bottega.factory.stock.forecast.ressource;
|
package pl.com.bottega.factory.stock.forecast.ressource;
|
||||||
|
|
||||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||||
|
import org.springframework.data.rest.core.config.Projection;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import pl.com.bottega.tools.ProjectionRepository;
|
import pl.com.bottega.tools.ProjectionRepository;
|
||||||
|
|
||||||
@@ -8,7 +9,12 @@ import pl.com.bottega.tools.ProjectionRepository;
|
|||||||
@RepositoryRestResource(path = "stock-forecasts",
|
@RepositoryRestResource(path = "stock-forecasts",
|
||||||
collectionResourceRel = "stock-forecasts",
|
collectionResourceRel = "stock-forecasts",
|
||||||
itemResourceRel = "stock-forecast",
|
itemResourceRel = "stock-forecast",
|
||||||
excerptProjection = StockForecastEntity.CollectionItem.class)
|
excerptProjection = StockForecastDao.CollectionItem.class)
|
||||||
public interface StockForecastDao extends ProjectionRepository<StockForecastEntity, String> {
|
public interface StockForecastDao extends ProjectionRepository<StockForecastEntity, String> {
|
||||||
|
|
||||||
|
@Projection(types = {StockForecastEntity.class})
|
||||||
|
interface CollectionItem {
|
||||||
|
String getRefNo();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package pl.com.bottega.factory.stock.forecast.ressource;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.data.rest.core.config.Projection;
|
import lombok.Setter;
|
||||||
import pl.com.bottega.factory.stock.forecast.StockForecast;
|
import pl.com.bottega.factory.stock.forecast.StockForecast;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
@@ -18,17 +18,10 @@ public class StockForecastEntity implements Serializable {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
private String refNo;
|
private String refNo;
|
||||||
|
@Setter
|
||||||
|
private transient StockForecast stockForecast;
|
||||||
|
|
||||||
public StockForecastEntity(String refNo) {
|
public StockForecastEntity(String refNo) {
|
||||||
this.refNo = refNo;
|
this.refNo = refNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StockForecast getStockForecast() {
|
|
||||||
return StaticAccess.calculateQuery(refNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Projection(types = {StockForecastEntity.class})
|
|
||||||
interface CollectionItem {
|
|
||||||
String getRefNo();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package pl.com.bottega.factory.stock.forecast.ressource;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.hateoas.Resource;
|
||||||
|
import org.springframework.hateoas.ResourceProcessor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import pl.com.bottega.factory.product.management.RefNoId;
|
||||||
|
import pl.com.bottega.factory.stock.forecast.StockForecastQuery;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
|
class StockForecastResourceProcessor implements ResourceProcessor<Resource<StockForecastEntity>> {
|
||||||
|
|
||||||
|
private final StockForecastQuery query;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource<StockForecastEntity> process(Resource<StockForecastEntity> resource) {
|
||||||
|
resource.getContent().setStockForecast(
|
||||||
|
query.get(new RefNoId(resource.getContent().getRefNo()))
|
||||||
|
);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 177 KiB |
@@ -22,7 +22,7 @@ public class DeliveryForecastProjection {
|
|||||||
private final CurrentDemandDao demandDao;
|
private final CurrentDemandDao demandDao;
|
||||||
private final DeliveryAutoPlannerORMRepository planners;
|
private final DeliveryAutoPlannerORMRepository planners;
|
||||||
|
|
||||||
public void persistDeliveryForecasts(DemandedLevelsChanged event) {
|
public void applyDemandedLevelsChanged(DemandedLevelsChanged event) {
|
||||||
DeliveryAutoPlanner planner = planners.get(event.getRefNo().getRefNo());
|
DeliveryAutoPlanner planner = planners.get(event.getRefNo().getRefNo());
|
||||||
event.getResults().keySet()
|
event.getResults().keySet()
|
||||||
.forEach(daily -> forecastDao.deleteByRefNoAndDate(
|
.forEach(daily -> forecastDao.deleteByRefNoAndDate(
|
||||||
@@ -42,7 +42,7 @@ public class DeliveryForecastProjection {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleDeliveryPlannerDefinitionChange(String refNo) {
|
public void applyDeliveryPlannerDefinitionChange(String refNo) {
|
||||||
List<CurrentDemandEntity> demands = demandDao.findByRefNoAndDateGreaterThanEqual(refNo, LocalDate.now(clock));
|
List<CurrentDemandEntity> demands = demandDao.findByRefNoAndDateGreaterThanEqual(refNo, LocalDate.now(clock));
|
||||||
DeliveryAutoPlanner planner = planners.get(refNo);
|
DeliveryAutoPlanner planner = planners.get(refNo);
|
||||||
forecastDao.deleteByRefNo(refNo);
|
forecastDao.deleteByRefNo(refNo);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import java.time.LocalDate;
|
|||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RepositoryEventHandler
|
@RepositoryEventHandler
|
||||||
public class Handler {
|
public class CommandsHandler {
|
||||||
|
|
||||||
private final DemandService service;
|
private final DemandService service;
|
||||||
private final DemandAdjustmentDao adjustments;
|
private final DemandAdjustmentDao adjustments;
|
||||||
@@ -10,7 +10,7 @@ public class CurrentDemandProjection {
|
|||||||
|
|
||||||
private final CurrentDemandDao demandDao;
|
private final CurrentDemandDao demandDao;
|
||||||
|
|
||||||
public void persistCurrentDemands(DemandedLevelsChanged event) {
|
public void applyDemandedLevelsChanged(DemandedLevelsChanged event) {
|
||||||
event.getResults().forEach((daily, change) -> {
|
event.getResults().forEach((daily, change) -> {
|
||||||
demandDao.deleteByRefNoAndDate(
|
demandDao.deleteByRefNoAndDate(
|
||||||
daily.getRefNo(),
|
daily.getRefNo(),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package pl.com.bottega.factory.demand.forecasting;
|
package pl.com.bottega.factory.demand.forecasting;
|
||||||
|
|
||||||
import lombok.Value;
|
import lombok.AllArgsConstructor;
|
||||||
import pl.com.bottega.factory.demand.forecasting.DailyDemand.Result;
|
import pl.com.bottega.factory.demand.forecasting.DailyDemand.Result;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@@ -11,12 +11,12 @@ import java.util.Optional;
|
|||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Value
|
@AllArgsConstructor
|
||||||
public class AdjustDemand {
|
public class AdjustDemand {
|
||||||
String refNo;
|
private final String refNo;
|
||||||
Map<LocalDate, Adjustment> adjustments;
|
private final Map<LocalDate, Adjustment> adjustments;
|
||||||
|
|
||||||
public List<Result> forEachStartingFrom(LocalDate date, BiFunction<LocalDate, Adjustment, Result> f) {
|
List<Result> forEachStartingFrom(LocalDate date, BiFunction<LocalDate, Adjustment, Result> f) {
|
||||||
return adjustments.entrySet().stream()
|
return adjustments.entrySet().stream()
|
||||||
.filter(e -> !e.getKey().isBefore(date))
|
.filter(e -> !e.getKey().isBefore(date))
|
||||||
.map(e -> f.apply(e.getKey(), e.getValue()))
|
.map(e -> f.apply(e.getKey(), e.getValue()))
|
||||||
@@ -24,6 +24,11 @@ public class AdjustDemand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<LocalDate> latestAdjustment() {
|
public Optional<LocalDate> latestAdjustment() {
|
||||||
return adjustments.keySet().stream().max(Comparator.naturalOrder());
|
return adjustments.keySet().stream()
|
||||||
|
.max(Comparator.naturalOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRefNo() {
|
||||||
|
return refNo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,30 @@
|
|||||||
package pl.com.bottega.factory.demand.forecasting;
|
package pl.com.bottega.factory.demand.forecasting;
|
||||||
|
|
||||||
import lombok.Value;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Value
|
@AllArgsConstructor
|
||||||
public class Document {
|
public class Document {
|
||||||
|
|
||||||
Instant created;
|
private final Instant created;
|
||||||
String refNo;
|
private final String refNo;
|
||||||
SortedMap<LocalDate, Demand> demands;
|
private final SortedMap<LocalDate, Demand> demands;
|
||||||
|
|
||||||
public Document(Instant created, String refNo, SortedMap<LocalDate, Demand> demands) {
|
List<DailyDemand.Result> forEachStartingFrom(LocalDate date, BiFunction<LocalDate, Demand, DailyDemand.Result> f) {
|
||||||
this.created = created;
|
|
||||||
this.refNo = refNo;
|
|
||||||
this.demands = Collections.unmodifiableSortedMap(demands);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DailyDemand.Result> forEachStartingFrom(LocalDate date, BiFunction<LocalDate, Demand, DailyDemand.Result> f) {
|
|
||||||
return demands.entrySet().stream()
|
return demands.entrySet().stream()
|
||||||
.filter(e -> !e.getKey().isBefore(date))
|
.filter(e -> !e.getKey().isBefore(date))
|
||||||
.map(e -> f.apply(e.getKey(), e.getValue()))
|
.map(e -> f.apply(e.getKey(), e.getValue()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRefNo() {
|
||||||
|
return refNo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
hexagon.png
BIN
hexagon.png
Binary file not shown.
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 296 KiB |
Reference in New Issue
Block a user