diff --git a/layers/etc/layers.png b/layers/etc/layers.png
new file mode 100644
index 000000000..a4bd8b19d
Binary files /dev/null and b/layers/etc/layers.png differ
diff --git a/layers/etc/layers.ucls b/layers/etc/layers.ucls
new file mode 100644
index 000000000..060b391c0
--- /dev/null
+++ b/layers/etc/layers.ucls
@@ -0,0 +1,256 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layers/index.md b/layers/index.md
new file mode 100644
index 000000000..d97406672
--- /dev/null
+++ b/layers/index.md
@@ -0,0 +1,19 @@
+---
+layout: pattern
+title: Layers
+folder: layers
+permalink: /patterns/layers/
+categories: pattern_cat
+tags: pattern_tag
+---
+
+**Intent:** Layers is an architectural style where software responsibilities are
+ divided among the different layers of the application.
+
+
+
+**Applicability:** Use the Layers architecture when
+
+* you want clearly divide software responsibilities into differents parts of the program
+* you want to prevent a change from propagating throughout the application
+* you want to make your application more maintainable and testable
diff --git a/layers/pom.xml b/layers/pom.xml
new file mode 100644
index 000000000..dd036e74c
--- /dev/null
+++ b/layers/pom.xml
@@ -0,0 +1,36 @@
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.5.0
+
+ com.iluwatar.layers
+ layers
+
+
+ org.springframework.data
+ spring-data-jpa
+
+
+ org.hibernate
+ hibernate-entitymanager
+
+
+ commons-dbcp
+ commons-dbcp
+
+
+ com.h2database
+ h2
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/layers/src/main/java/com/iluwatar/layers/App.java b/layers/src/main/java/com/iluwatar/layers/App.java
new file mode 100644
index 000000000..3ab5e211e
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/App.java
@@ -0,0 +1,108 @@
+package com.iluwatar.layers;
+
+import java.util.Arrays;
+
+/**
+ *
+ *
+ * Layers is an architectural style where software responsibilities are
+ * divided among the different layers of the application.
+ *
+ *
+ *
+ * This example demonstrates a traditional 3-layer architecture consisting of data access
+ * layer, business layer and presentation layer.
+ *
+ *
+ *
+ * The data access layer is formed of Spring Data repositories CakeDao
, CakeToppingDao
and
+ * CakeLayerDao
. The repositories can be used for CRUD operations on cakes, cake toppings
+ * and cake layers respectively.
+ *
+ *
+ *
+ * The business layer is built on top of the data access layer. CakeBakingService
offers
+ * methods to retrieve available cake toppings and cake layers and baked cakes. Also the
+ * service is used to create new cakes out of cake toppings and cake layers.
+ *
+ *
+ *
+ * The presentation layer is built on the business layer and in this example it simply lists
+ * the cakes that have been baked.
+ *
+ *
+ *
+ * We have applied so called strict layering which means that the layers can only access
+ * the classes directly beneath them. This leads the solution to create an additional set of
+ * DTOs (CakeInfo
, CakeToppingInfo
, CakeLayerInfo
)
+ * to translate data between layers. In other words, CakeBakingService
cannot
+ * return entities (Cake
, CakeTopping
, CakeLayer
)
+ * directly since these reside on data access layer but instead translates these into business
+ * layer DTOs (CakeInfo
, CakeToppingInfo
, CakeLayerInfo
)
+ * and returns them instead. This way the presentation layer does not have any knowledge of
+ * other layers than the business layer and thus is not affected by changes to them.
+ *
+ *
+ * @see Cake
+ * @see CakeTopping
+ * @see CakeLayer
+ * @see CakeDao
+ * @see CakeToppingDao
+ * @see CakeLayerDao
+ * @see CakeBakingService
+ * @see CakeInfo
+ * @see CakeToppingInfo
+ * @see CakeLayerInfo
+ *
+ */
+public class App {
+
+ private static CakeBakingService cakeBakingService = new CakeBakingServiceImpl();
+
+ /**
+ * Application entry point
+ * @param args Command line parameters
+ */
+ public static void main(String[] args) {
+
+ // initialize example data
+ initializeData(cakeBakingService);
+
+ // create view and render it
+ CakeViewImpl cakeView = new CakeViewImpl(cakeBakingService);
+ cakeView.render();
+ }
+
+ /**
+ * Initializes the example data
+ * @param cakeBakingService
+ */
+ private static void initializeData(CakeBakingService cakeBakingService) {
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200));
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900));
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950));
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950));
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950));
+ cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950));
+
+ cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350));
+ cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350));
+
+ CakeInfo cake1 = new CakeInfo(new CakeToppingInfo("candies", 0),
+ Arrays.asList(new CakeLayerInfo("chocolate", 0), new CakeLayerInfo("banana", 0),
+ new CakeLayerInfo("strawberry", 0)));
+ try {
+ cakeBakingService.bakeNewCake(cake1);
+ } catch (CakeBakingException e) {
+ e.printStackTrace();
+ }
+ CakeInfo cake2 = new CakeInfo(new CakeToppingInfo("cherry", 0),
+ Arrays.asList(new CakeLayerInfo("vanilla", 0), new CakeLayerInfo("lemon", 0),
+ new CakeLayerInfo("strawberry", 0)));
+ try {
+ cakeBakingService.bakeNewCake(cake2);
+ } catch (CakeBakingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/Cake.java b/layers/src/main/java/com/iluwatar/layers/Cake.java
new file mode 100644
index 000000000..193ba5e3f
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/Cake.java
@@ -0,0 +1,68 @@
+package com.iluwatar.layers;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+
+/**
+ *
+ * Cake entity
+ *
+ */
+@Entity
+public class Cake {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ @OneToOne(cascade=CascadeType.REMOVE)
+ private CakeTopping topping;
+
+ @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER)
+ private Set layers;
+
+ public Cake() {
+ setLayers(new HashSet<>());
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public CakeTopping getTopping() {
+ return topping;
+ }
+
+ public void setTopping(CakeTopping topping) {
+ this.topping = topping;
+ }
+
+ public Set getLayers() {
+ return layers;
+ }
+
+ public void setLayers(Set layers) {
+ this.layers = layers;
+ }
+
+ public void addLayer(CakeLayer layer) {
+ this.layers.add(layer);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString());
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java
new file mode 100644
index 000000000..0a44d56f9
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java
@@ -0,0 +1,18 @@
+package com.iluwatar.layers;
+
+/**
+ *
+ * Custom exception used in cake baking
+ *
+ */
+public class CakeBakingException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public CakeBakingException() {
+ }
+
+ public CakeBakingException(String message) {
+ super(message);
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java
new file mode 100644
index 000000000..fec16b494
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java
@@ -0,0 +1,48 @@
+package com.iluwatar.layers;
+
+import java.util.List;
+
+/**
+ *
+ * Service for cake baking operations
+ *
+ */
+public interface CakeBakingService {
+
+ /**
+ * Bakes new cake according to parameters
+ * @param cakeInfo
+ * @throws CakeBakingException
+ */
+ void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException;
+
+ /**
+ * Get all cakes
+ * @return
+ */
+ List getAllCakes();
+
+ /**
+ * Store new cake topping
+ * @param toppingInfo
+ */
+ void saveNewTopping(CakeToppingInfo toppingInfo);
+
+ /**
+ * Get available cake toppings
+ * @return
+ */
+ List getAvailableToppings();
+
+ /**
+ * Add new cake layer
+ * @param layerInfo
+ */
+ void saveNewLayer(CakeLayerInfo layerInfo);
+
+ /**
+ * Get available cake layers
+ * @return
+ */
+ List getAvailableLayers();
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java
new file mode 100644
index 000000000..7e5e3dcff
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java
@@ -0,0 +1,149 @@
+package com.iluwatar.layers;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ *
+ * Implementation of CakeBakingService
+ *
+ */
+@Service
+@Transactional
+public class CakeBakingServiceImpl implements CakeBakingService {
+
+ private AbstractApplicationContext context;
+
+ public CakeBakingServiceImpl() {
+ this.context = new ClassPathXmlApplicationContext("applicationContext.xml");
+ }
+
+ @Override
+ public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException {
+ List allToppings = getAvailableToppings();
+ List matchingToppings = allToppings.stream()
+ .filter((t) -> t.name.equals(cakeInfo.cakeToppingInfo.name)).collect(Collectors.toList());
+ if (matchingToppings.isEmpty()) {
+ throw new CakeBakingException(String.format("Topping %s is not available", cakeInfo.cakeToppingInfo.name));
+ }
+ List allLayers = getAvailableLayerEntities();
+ Set foundLayers = new HashSet<>();
+ for (CakeLayerInfo info: cakeInfo.cakeLayerInfos) {
+ Optional found = allLayers.stream().filter((layer) -> layer.getName().equals(info.name)).findFirst();
+ if (!found.isPresent()) {
+ throw new CakeBakingException(String.format("Layer %s is not available", info.name));
+ } else {
+ foundLayers.add(found.get());
+ }
+ }
+ CakeToppingDao toppingBean = context.getBean(CakeToppingDao.class);
+ CakeTopping topping = toppingBean.findOne(matchingToppings.iterator().next().id.get());
+ CakeDao cakeBean = context.getBean(CakeDao.class);
+ Cake cake = new Cake();
+ cake.setTopping(topping);
+ cake.setLayers(foundLayers);
+ cakeBean.save(cake);
+ topping.setCake(cake);
+ toppingBean.save(topping);
+ CakeLayerDao layerBean = context.getBean(CakeLayerDao.class);
+ for (CakeLayer layer: foundLayers) {
+ layer.setCake(cake);
+ layerBean.save(layer);
+ }
+ }
+
+ @Override
+ public void saveNewTopping(CakeToppingInfo toppingInfo) {
+ CakeToppingDao bean = context.getBean(CakeToppingDao.class);
+ bean.save(new CakeTopping(toppingInfo.name, toppingInfo.calories));
+ }
+
+ @Override
+ public void saveNewLayer(CakeLayerInfo layerInfo) {
+ CakeLayerDao bean = context.getBean(CakeLayerDao.class);
+ bean.save(new CakeLayer(layerInfo.name, layerInfo.calories));
+ }
+
+ private List getAvailableToppingEntities() {
+ CakeToppingDao bean = context.getBean(CakeToppingDao.class);
+ List result = new ArrayList<>();
+ Iterator iterator = bean.findAll().iterator();
+ while (iterator.hasNext()) {
+ CakeTopping topping = iterator.next();
+ if (topping.getCake() == null) {
+ result.add(topping);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List getAvailableToppings() {
+ CakeToppingDao bean = context.getBean(CakeToppingDao.class);
+ List result = new ArrayList<>();
+ Iterator iterator = bean.findAll().iterator();
+ while (iterator.hasNext()) {
+ CakeTopping next = iterator.next();
+ if (next.getCake() == null) {
+ result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories()));
+ }
+ }
+ return result;
+ }
+
+ private List getAvailableLayerEntities() {
+ CakeLayerDao bean = context.getBean(CakeLayerDao.class);
+ List result = new ArrayList<>();
+ Iterator iterator = bean.findAll().iterator();
+ while (iterator.hasNext()) {
+ CakeLayer next = iterator.next();
+ if (next.getCake() == null) {
+ result.add(next);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List getAvailableLayers() {
+ CakeLayerDao bean = context.getBean(CakeLayerDao.class);
+ List result = new ArrayList<>();
+ Iterator iterator = bean.findAll().iterator();
+ while (iterator.hasNext()) {
+ CakeLayer next = iterator.next();
+ if (next.getCake() == null) {
+ result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories()));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List getAllCakes() {
+ CakeDao cakeBean = context.getBean(CakeDao.class);
+ List result = new ArrayList<>();
+ Iterator iterator = cakeBean.findAll().iterator();
+ while (iterator.hasNext()) {
+ Cake cake = iterator.next();
+ CakeToppingInfo cakeToppingInfo = new CakeToppingInfo(cake.getTopping().getId(),
+ cake.getTopping().getName(), cake.getTopping().getCalories());
+ ArrayList cakeLayerInfos = new ArrayList();
+ for (CakeLayer layer: cake.getLayers()) {
+ cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories()));
+ }
+ CakeInfo cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos);
+ result.add(cakeInfo);
+ }
+ return result;
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeDao.java b/layers/src/main/java/com/iluwatar/layers/CakeDao.java
new file mode 100644
index 000000000..075e75d31
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeDao.java
@@ -0,0 +1,14 @@
+package com.iluwatar.layers;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ *
+ * CRUD repository for cakes
+ *
+ */
+@Repository
+public interface CakeDao extends CrudRepository {
+
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java
new file mode 100644
index 000000000..335ce5f4f
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java
@@ -0,0 +1,40 @@
+package com.iluwatar.layers;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ *
+ * DTO for cakes
+ *
+ */
+public class CakeInfo {
+
+ public final Optional id;
+ public final CakeToppingInfo cakeToppingInfo;
+ public final List cakeLayerInfos;
+
+ public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) {
+ this.id = Optional.of(id);
+ this.cakeToppingInfo = cakeToppingInfo;
+ this.cakeLayerInfos = cakeLayerInfos;
+ }
+
+ public CakeInfo(CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) {
+ this.id = Optional.empty();
+ this.cakeToppingInfo = cakeToppingInfo;
+ this.cakeLayerInfos = cakeLayerInfos;
+ }
+
+ public int calculateTotalCalories() {
+ int total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0;
+ total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum();
+ return total;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.get(), cakeToppingInfo,
+ cakeLayerInfos, calculateTotalCalories());
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayer.java b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java
new file mode 100644
index 000000000..2f8649c18
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java
@@ -0,0 +1,72 @@
+package com.iluwatar.layers;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+/**
+ *
+ * CakeLayer entity
+ *
+ */
+@Entity
+public class CakeLayer {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ private String name;
+
+ private int calories;
+
+ @ManyToOne(cascade = CascadeType.ALL)
+ private Cake cake;
+
+ public CakeLayer() {
+ }
+
+ public CakeLayer(String name, int calories) {
+ this.setName(name);
+ this.setCalories(calories);
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getCalories() {
+ return calories;
+ }
+
+ public void setCalories(int calories) {
+ this.calories = calories;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("id=%s name=%s calories=%d", id, name, calories);
+ }
+
+ public Cake getCake() {
+ return cake;
+ }
+
+ public void setCake(Cake cake) {
+ this.cake = cake;
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java
new file mode 100644
index 000000000..9e1d035a8
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java
@@ -0,0 +1,14 @@
+package com.iluwatar.layers;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ *
+ * CRUD repository for cake layers
+ *
+ */
+@Repository
+public interface CakeLayerDao extends CrudRepository {
+
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java
new file mode 100644
index 000000000..9aa7ff7f6
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java
@@ -0,0 +1,32 @@
+package com.iluwatar.layers;
+
+import java.util.Optional;
+
+/**
+ *
+ * DTO for cake layers
+ *
+ */
+public class CakeLayerInfo {
+
+ public final Optional id;
+ public final String name;
+ public final int calories;
+
+ public CakeLayerInfo(Long id, String name, int calories) {
+ this.id = Optional.of(id);
+ this.name = name;
+ this.calories = calories;
+ }
+
+ public CakeLayerInfo(String name, int calories) {
+ this.id = Optional.empty();
+ this.name = name;
+ this.calories = calories;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.get(), name, calories);
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeTopping.java b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java
new file mode 100644
index 000000000..f0e30997a
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java
@@ -0,0 +1,72 @@
+package com.iluwatar.layers;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+/**
+ *
+ * CakeTopping entity
+ *
+ */
+@Entity
+public class CakeTopping {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ private String name;
+
+ private int calories;
+
+ @OneToOne(cascade = CascadeType.ALL)
+ private Cake cake;
+
+ public CakeTopping() {
+ }
+
+ public CakeTopping(String name, int calories) {
+ this.setName(name);
+ this.setCalories(calories);
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getCalories() {
+ return calories;
+ }
+
+ public void setCalories(int calories) {
+ this.calories = calories;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("id=%s name=%s calories=%d", name, calories);
+ }
+
+ public Cake getCake() {
+ return cake;
+ }
+
+ public void setCake(Cake cake) {
+ this.cake = cake;
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java
new file mode 100644
index 000000000..3ddcf53ec
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java
@@ -0,0 +1,14 @@
+package com.iluwatar.layers;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ *
+ * CRUD repository cake toppings
+ *
+ */
+@Repository
+public interface CakeToppingDao extends CrudRepository {
+
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java
new file mode 100644
index 000000000..152b0ff85
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java
@@ -0,0 +1,32 @@
+package com.iluwatar.layers;
+
+import java.util.Optional;
+
+/**
+ *
+ * DTO for cake toppings
+ *
+ */
+public class CakeToppingInfo {
+
+ public final Optional id;
+ public final String name;
+ public final int calories;
+
+ public CakeToppingInfo(Long id, String name, int calories) {
+ this.id = Optional.of(id);
+ this.name = name;
+ this.calories = calories;
+ }
+
+ public CakeToppingInfo(String name, int calories) {
+ this.id = Optional.empty();
+ this.name = name;
+ this.calories = calories;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CakeToppingInfo id=%d name=%s calories=%d", id.get(), name, calories);
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java
new file mode 100644
index 000000000..5fed15c3a
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java
@@ -0,0 +1,19 @@
+package com.iluwatar.layers;
+
+/**
+ *
+ * View implementation for displaying cakes
+ *
+ */
+public class CakeViewImpl implements View {
+
+ private CakeBakingService cakeBakingService;
+
+ public CakeViewImpl(CakeBakingService cakeBakingService) {
+ this.cakeBakingService = cakeBakingService;
+ }
+
+ public void render() {
+ cakeBakingService.getAllCakes().stream().forEach((cake) -> System.out.println(cake));
+ }
+}
diff --git a/layers/src/main/java/com/iluwatar/layers/View.java b/layers/src/main/java/com/iluwatar/layers/View.java
new file mode 100644
index 000000000..123d4ecbf
--- /dev/null
+++ b/layers/src/main/java/com/iluwatar/layers/View.java
@@ -0,0 +1,12 @@
+package com.iluwatar.layers;
+
+/**
+ *
+ * View interface
+ *
+ */
+public interface View {
+
+ void render();
+
+}
diff --git a/layers/src/main/resources/META-INF/persistence.xml b/layers/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 000000000..d94d8582b
--- /dev/null
+++ b/layers/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layers/src/main/resources/applicationContext.xml b/layers/src/main/resources/applicationContext.xml
new file mode 100644
index 000000000..0c908ad2e
--- /dev/null
+++ b/layers/src/main/resources/applicationContext.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 1059edc58..de87da567 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,7 @@
business-delegate
half-sync-half-async
step-builder
+ layers