This document provides information about Jarosław Ratajski, including that he is a developer who lives in Luzern and works at CSS Insurance. It also discusses an alien pizza delivery domain model called Galakpizza that was developed using two different approaches - one using plain Java objects ("Normal") and the other using a relational database and JPA ("Other"). The domain model includes concepts like Orders, Variants, Sizes and Planets. The services implemented allow placing orders and retrieving the orders from the best planet.
7. 10 000 Planets 3 Variants
3 Sizes
HAWAII
MARGHERITTA
VEGETARIAN
MEDIUM
LARGE
XL
Domain summary
8. MARGHERITTA MEDIUM
HAWAII LARGE
HAWAII LARGE
VEGERARIAN LARGE
HAWAII XL
MARGHERITTA XL
HAWAII LARGE
HAWAII LARGE
HAWAII LARGE
HAWAII LARGE
33
33
11
11
Delivery model
9. 10 000 Pizzas / s
I want to look at number
of queued orders 10000 times / s
My wife also!
Requirements
13. Normal domain
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
public final long id;
public final String planet;
public final Variant variant;
public final Size size;
public Order(long id, String planet, Variant variant, Size size) {
this.id = id;
this.planet = planet;
this.size = size;
this.variant = variant;
}
}
14. Normal domain 2
public enum Size {
MEDIUM,
LARGE,
XL
}
public enum Variant {
HAWAII,
MARGHERITA,
VEGETARIAN
}
16. Normal Service
public interface GalakPizzaOrders {
long placeOrder(String planet, Variant variant, Size size);
}
public interface GalakPizzaDelivery {
List<Order> takeOrdersFromBestPlanet();
long countStandingOrders();
}
17. Normal Service
public interface GalakPizzaOrders {
long placeOrder(String planet, Variant variant, Size size);
}
public interface GalakPizzaDelivery {
List<Order> takeOrdersFromBestPlanet();
long countStandingOrders();
}
public interface GalakPizzaService extends GalakPizzaDelivery,
GalakPizzaOrders{
}
18. Normal service implementation...
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
public long placeOrder(String planet, Variant variant, Size size) {
...
}
public List<Order> takeOrdersFromBestPlanet() {
...
}
public long countStandingOrders() {
...
}
}
19. Normal service implementation...
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
private finalprivate final Map<String, List<Order>>Map<String, List<Order>> ordersorders == newnew HashMap<>();HashMap<>();
public long placeOrder(String planet, Variant variant, Size size) {
...
}
public List<Order> takeOrdersFromBestPlanet() {
...
}
public long countStandingOrders() {
...
}
}
20. Normal service implementation...
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
private finalprivate final Map<String, PlanetOrders>Map<String, PlanetOrders> ordersorders == newnew HashMap<>();HashMap<>();
public long placeOrder(String planet, Variant variant, Size size) {
...
}
public List<Order> takeOrdersFromBestPlanet() {
...
}
public long countStandingOrders() {
...
}
}
21. PlanetOrders - helper
class PlanetOrders implements Serializable {
final String name;
private List<Order> orders = new ArrayList<>();
public PlanetOrders(String name) {
this.name = name;
}
...
}
22. PlanetOrders - helper
class PlanetOrders implements Serializable {
final String name;
private List<Order> orders = new ArrayList<>();
public PlanetOrders(String name) {
this.name = name;
}
void assignOrder(final Order order) {
this.orders.add(order);
}
...
}
23. PlanetOrders - helper
class PlanetOrders implements Serializable {
final String name;
private List<Order> orders = new ArrayList<>();
public PlanetOrders(String name) {
this.name = name;
}
void assignOrder(final Order order) {
this.orders.add(order);
}
public boolean isEmpty() {
return this.orders.isEmpty();
}
...
}
24. Normal place order
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
private final Map<String, PlanetOrders> orders = new HashMap<>();
private long orderSequence = 1;
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
return id;
}
25. Normal place order
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
private final Map<String, PlanetOrders> orders = new HashMap<>();
private long orderSequence = 1;
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
return id;
}
private void assignOrderToPlanet(Order order) {
final PlanetOrders po = orders.computeIfAbsent(order.planet,
planetName -> new PlanetOrders(planetName));
po.assignOrder(order);
...
26. Normal place order
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
private final Map<String, PlanetOrders> orders = new HashMap<>();
private long orderSequence = 1;
private AtomicLong ordersTotal = new AtomicLong(0);
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
ordersTotal.incrementAndGet();
return id;
}
private void assignOrderToPlanet(Order order) {
final PlanetOrders po = orders.computeIfAbsent(order.planet,
planetName -> new PlanetOrders(planetName));
po.assignOrder(order);
...
27. Normal place order revisited
public class GalakPizzaCore { ...
private final Map<String, PlanetOrders> orders = new HashMap<>();
private PriorityQueue<PlanetOrders> bestPlanets = new PriorityQueue<>(256);
private long orderSequence = 1;
private AtomicLong ordersTotal = new AtomicLong(0);
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
ordersTotal.incrementAndGet();
return id;
}
private void assignOrderToPlanet(Order order) {
final PlanetOrders po = orders.computeIfAbsent(order.planet,
planetName -> new PlanetOrders(planetName));
po.assignOrder(order);
this.bestPlanets.offer(po);
}
28. Normal place order revisited
public class GalakPizzaCore { ...
private final Map<String, PlanetOrders> orders = new HashMap<>();
private PriorityQueue<PlanetOrders> bestPlanets = new PriorityQueue<>(256);
private long orderSequence = 1;
private AtomicLong ordersTotal = new AtomicLong(0);
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
ordersTotal.incrementAndGet();
return id;
}
private void assignOrderToPlanet(Order order) {
final PlanetOrders po = orders.computeIfAbsent(order.planet,
planetName -> new PlanetOrders(planetName));
po.assignOrder(order);
this.bestPlanets.offer(po); BAD BAD BAD BAD
}
29. Normal place order revisited
public class GalakPizzaCore { ...
private final Map<String, PlanetOrders> orders = new HashMap<>();
private PriorityQueue<PlanetOrders.Wrapper> bestPlanets = new PriorityQueue<>(256);
private long orderSequence = 1;
private AtomicLong ordersTotal = new AtomicLong(0);
public long placeOrder(String planet, Variant variant, Size size) {
final long id = orderSequence++;
final Order order = new Order(id, planet, variant, size);
assignOrderToPlanet(order);
ordersTotal.incrementAndGet();
return id;
}
private void assignOrderToPlanet(Order order) {
final PlanetOrders po = orders.computeIfAbsent(order.planet,
planetName -> new PlanetOrders(planetName));
po.assignOrder(order);
this.bestPlanets.offer(new PlanetOrders.Wrapper(po));
}
30. Wrapper... yeah
public static class Wrapper implements Comparable<Wrapper>,Serializable{
final PlanetOrders porders;
final int size; //just keep it final
public Wrapper(PlanetOrders porders) {
this.porders = porders;
this.size = porders.orders.size();
}
@Override
public int compareTo(final Wrapper other) {
return other.size - this.size;
}
}
31. PlanetOrders – take orders
class PlanetOrders implements Serializable {
final String name;
private List<Order> orders = new ArrayList<>();
List<Order> takeOrders() {
final List<Order> result = this.orders;
this.orders = new ArrayList<>();
return result;
}
public boolean isEmpty() {
return this.orders.isEmpty();
}
}
32. PlanetOrders – take orders
class PlanetOrders implements Serializable {
final String name;
private List<Order> orders = new ArrayList<>();
List<Order> takeOrders() {
final List<Order> result = this.orders;
this.orders = new ArrayList<>(); //yes we MUTATE!
return result;
}
public boolean isEmpty() {
return this.orders.isEmpty();
}
}
33. Normal take orders
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
privateprivate PriorityQueue<PlanetOrders.Wrapper>PriorityQueue<PlanetOrders.Wrapper> bestPlanetsbestPlanets == newnew
PriorityQueue<>(PriorityQueue<>(256256););
private AtomicLong ordersTotal = new AtomicLong(0);
public List<Order> takeOrdersFromBestPlanet() {
final Optional<PlanetOrders> planetOpt = takeBestPlanet();
....
}
private Optional<PlanetOrders> takeBestPlanet() {
PlanetOrders.Wrapper planet = this.bestPlanets.poll();
while (planet != null && planet.porders.isEmpty()) {
planet = this.bestPlanets.poll();
}
return Optional.ofNullable(planet).map(p->p.porders);
}
34. Normal take orders
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
privateprivate PriorityQueue<PlanetOrders.Wrapper>PriorityQueue<PlanetOrders.Wrapper> bestPlanetsbestPlanets == newnew
PriorityQueue<>(PriorityQueue<>(256256););
private AtomicLong ordersTotal = new AtomicLong(0);
public List<Order> takeOrdersFromBestPlanet() {
final Optional<PlanetOrders> planetOpt = takeBestPlanet();
if (planetOpt.isPresent()) {
final PlanetOrders planet = planetOpt.get();
List<Order> orders = planet.takeOrders();
return orders;
}
return Collections.EMPTY_LIST;
}
private Optional<PlanetOrders> takeBestPlanet() {
PlanetOrders.Wrapper planet = this.bestPlanets.poll();
while (planet != null && planet.porders.isEmpty()) {
planet = this.bestPlanets.poll();
}
return Optional.ofNullable(planet).map(p->p.porders);
}
35. Normal take orders
public class GalakPizzaCore implements GalakPizzaService, Serializable,
Storable<GalakPizzaCore> {
privateprivate PriorityQueue<PlanetOrders.Wrapper>PriorityQueue<PlanetOrders.Wrapper> bestPlanetsbestPlanets == newnew
PriorityQueue<>(PriorityQueue<>(256256););
private AtomicLong ordersTotal = new AtomicLong(0);
public List<Order> takeOrdersFromBestPlanet() {
final Optional<PlanetOrders> planetOpt = takeBestPlanet();
if (planetOpt.isPresent()) {
final PlanetOrders planet = planetOpt.get();
List<Order> orders = planet.takeOrders();
ordersTotal.addAndGet(-orders.size());
return orders;
}
return Collections.EMPTY_LIST;
}
private Optional<PlanetOrders> takeBestPlanet() {
PlanetOrders.Wrapper planet = this.bestPlanets.poll();
while (planet != null && planet.porders.isEmpty()) {
planet = this.bestPlanets.poll();
}
return Optional.ofNullable(planet).map(p->p.porders);
}
36. Normal count orders
private AtomicLong ordersTotal = new AtomicLong(0);
public long countStandingOrders() {
return this.ordersTotal.get();
}
37. We have service - but do we deal with?
Concurrency
Persistence
38. Normal - Alien service wrapper
public class GalakPizza implements GalakPizzaService {
final PersistenceController<GalakPizzaCoreGalakPizzaCore,GalakPizzaCore> controller;
public GalakPizza() {
controller = PrevaylerBuilder.newBuilder()
.withinUserFolder("pizza")
.useSupplier( () -> new GalakPizzaCore())
.build();
}
public long placeOrder(final String planet, final Variant variant, final Size size) {
return controller.executeAndQuery( core -> core.placeOrder(planet,variant,size)core.placeOrder(planet,variant,size));
}
public List<Order> takeOrdersFromBestPlanet() {
return controller.executeAndQuery( core ->core.takeOrdersFromBestPlanet()core.takeOrdersFromBestPlanet());
}
public long countStandingOrders() {
return controller.query( c->c.countStandingOrders()c.countStandingOrders());
}
...
}
41. Other Domain
@Entity
@Table(name="T_ORDER")
@NamedQueries( {
@NamedQuery(name = "select best planet", query = "SELECT o.planet, count(o) FROM Order o " +
" GROUP BY o.planet ORDER BY count(o) desc"),
@NamedQuery(name = "select orders", query = "SELECT o FROM Order o WHERE o.planet = :planet"),
@NamedQuery(name = "count orders", query = "SELECT count(o) FROM Order o ")
})
public class Order {
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
final long id;
public final String planet;
public final Variant variant;
public final Size size;
protected Order() {
this("this is strange...", null, null);
}
public Order(String planet, Variant variant, Size size) {
this.planet = planet;
this.variant = variant;
this.size = size;
id = 0;
}
public long getId() {
return id;
}
}
42. Other domain (same as above)
public enum Size {
MEDIUM,
LARGE,
XL
}
public enum Variant {
HAWAII,
MARGHERITA,
VEGETARIAN
}
44. Other service (same)
public interface GalakPizzaOrders {
long placeOrder(String planet, Variant variant, Size size);
}
public interface GalakPizzaDelivery {
List<Order> takeOrdersFromBestPlanet();
long countStandingOrders();
}
public interface GalakPizzaService extends GalakPizzaDelivery,
GalakPizzaOrders{
}
46. Other Planet (but almost same story)
@Entity
@Table(name="T_PLANET",
indexes = {@Index(name="cnt_idx", columnList = "count", unique = false)} )
@NamedQueries( {
@NamedQuery(name = "select best planet from table", query = "SELECT p FROM Planet p " +
" ORDER BY p.count desc")})
public class Planet {
@Id
public final String name;
private int count;
protected Planet() {
name = "default";
}
public Planet(String name) {
this.name = name;
}
public int getCount() {
return this.count;
}
public void increment() {
this.count++;
}
public void clear() {
this.count = 0;
}
}
47. Other service implementation
public class GalakPizza implements GalakPizzaService {
final SessionFactory sessionFactory;
public GalakPizza(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public long placeOrder(String planet, Variant variant, Size size) {
...
}
public List<Order> takeOrdersFromBestPlanet() {
...
}
public long countStandingOrders() {
...
}
}
48. Other - Place order
public long placeOrder(String planet, Variant variant, Size size) {
return this.runInSession(session -> {
final Order orderEntity = new Order(planet, variant, size);
final Long key = (Long) session.save(orderEntity);
incrementPlanetCounter(planet, session);
return key;
});
}
private void incrementPlanetCounter(String planet, Session session) {
Planet planetEntity = session.get(Planet.class, planet);
if (planetEntity == null) {
planetEntity = new Planet(planet);
session.save(planetEntity);
}
planetEntity.increment();
}
private <T> T runOnSession(Function<Session, T> dbCommand) { ...}
49. Other - runInSession
private <T> T runInSession(Function<Session, T> dbCommand) {
final Session session = sessionFactory.openSession();
session.beginTransaction();
try {
final T result = dbCommand.apply(session);
session.getTransaction().commit();
session.close();
return result;
} catch (ConstraintViolationException cve) {
session.getTransaction().rollback();
session.close();
return runInSession(dbCommand);
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException(t);
}
}
So easy
50. Other - Take orders
public List<Order> takeOrdersFromBestPlanet() {
return this.runInSession(session -> {
final Query bestPlanetQuery = session.getNamedQuery("SELECT p FROM Planet p " +
" ORDER BY p.count desc");
bestPlanetQuery.setMaxResults(1);
final Iterator<Planet> bestPlanetsIterator = bestPlanetQuery.iterate();
if (bestPlanetsIterator.hasNext()) {
final Planet bestOne = bestPlanetsIterator.next();
final Query ordersQuery = session.getNamedQuery("SELECT o FROM Order o WHERE
o.planet = :planet");
ordersQuery.setParameter("planet", bestOne.name);
final List<Order> orders = ordersQuery.list();
orders.forEach(o -> session.delete(o));
bestOne.clear();
return orders;
}
return Collections.EMPTY_LIST;
});
}
51. Other – count orders
@Override
public long countStandingOrders() {
return this.runInSession(session -> {
final Query ordersCount = session.getNamedQuery("count
orders");
final Long cnt = (Long) ordersCount.iterate().next();
return cnt;
});
}
55. That we can simulate as
Customers - 4 Threads doing in loop placeOrder (random planet
name, variant and size) – no pauses
PizzBoy - 1 Thread doing in loop takeBestOrder – no pauses
Owners – 2 Threads doing in loop – countStandingOrders
66. Reality strickes back
I do not trust PizzaBoy
I want to know where exactly were pizzas delivered
67. Normal universe Other universe
Cool, we will not delete
orders any more,
only mark them
as deleted!
Cool, we will add a list
that will be processed
during takeOrders,
or maybe
we will store data
in DB Table (as archive)
68. Reality strickes back
I do not trust PizzaBoy
I want to know where were pizzas delivered
Yesterday!!!
69. Normal universe Other universe
Crap...
We will restart system
and commands
will be reprocessed
71. System evolves
I want to let people compose their pizza/
Like: more cheese on one half,
and no meat on the next half,
And onion on the third half.
72. Normal universe Other universe
We will map it with only 20 tables
Or do we add TEXT field?
public Order(long id, String
planet, Supplier<Variant>
pizzaDescription, Size size) {
public Order(long id, String
planet, Variant variant, Size
size) {
We will use factory
73. Difference
+ Fast
+ Code in Java
+ „Strongly“ typed
+ Flexible
+ Testable
- Sophisticated java
- Terribly slow
- Code in Strings and
Annotations
- Stringly typed
- Limited domain
- Testing expensive
+ Easy java
76. Sweet spot known as 90's
Computers had mostly one CPU Core
Memory (RAM) was expensive but more as 100MB
available in single unit
Disks were slow but huge (like 2Gb-20Gb)
Time for JEE
78. Normal universe
RAM is so cheap and huge
Computers have multiple cores
Alien developers start to use full power of hardware with Java
No need to store data in databases anymore
Because RAM is so cheap and huge
79. Other universe
RAM is cheap and huge
Computers have multiple cores
But alien developers have never realised that
80. Other universe is a good place
Database vendors earn money and are happy
Application Servers vendors earn money and are happy
Power plants are happy
Hardware vendors are happy
Only business complains – but they still pay
Developers have a lot more work to do – they are happy..
Gfx By pressfoto / Freepik
81.
82. DB / ORM vs Prevalence Problems
Problem Prevalent (Normal) ORMs (Other)
Detached objects Yes Yes
Transaction
isolation
Yes in JAVA
(concurrency)
Yes in DB
Testing Trivial Awful
Cache It is all cache Lots of configs...
Magic Contained Everywhere
Mapping All serializable goes Limited
97. Indices
Indices in memory do exists
CQEngine
Query<Car> query1 = or(endsWith(Car.NAME, "vic"),
lessThan(Car.CAR_ID, 2));
for (Car car : cars.retrieve(query1)) {
System.out.println(car);
}
101. Storing in Prevayler vs DB
model events vs model domain
store facts vs store state
Shared state is a cache vs shared state is a problem
Domain with little magic vs domain polluted
Clean architecture vs Maybe DDD can help...
102. End
Try on something small first (not bigger then one planet)
Drop DB from core of your system
Drop your application servers
Drop annotations
Drop magic
Learn Java and algorithms
Feel the power of RAM and CPU alltogether!