Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Keeping business logic out of your UIs
1. Keeping business logic out of
your UIs
Petter Holmström, Vaadin Architect
petter@vaadin.com
2. What is “business logic”?
● Every information system exists to solve a real-world problem (I hope...)
● A real-world problem consists of static parts (things, concepts) and dynamic
parts (events, processes)
● A system uses a model of the real world to solve its problems
○ The domain model
● The static parts of the domain model are often the easy ones
● The dynamic parts are easily forgotten even though they are just as important
● The business logic is the implementation of the dynamic parts of the domain
model
10. Think about the transaction boundary
● Pay attention while coding and while reviewing others’ code
● The transaction boundary should IMO go between the UI and your backend
● Rules of thumb:
○ One UI operation, one backend call
○ One backend call, one transaction
○ One transaction, one thread
● Imagine your backend is running on a remote machine and you want to
minimize the traffic
11. Take care of your services
● Strive for highly cohesive services
● Avoid too general names like CustomerService
● Instead, opt for more specific names like CustomerOnboarding
● If you are adding a new service method and you find you could add it to more
than one service, you should probably create a completely new one or refactor
the existing ones
12. Separate reading and writing
● Place your queries in separate components
● Create UI specific query components instead of adding more and more query
methods to existing ones
● Allows for some neat optimizations and scaling
13. Avoid CRUD
● CRUD UIs and services basically move the business logic into the head of the
user
● Base your UIs and services on actual business processes and operations
● Exception: management UIs for reference and master data
15. ● Think in terms of Commands and Queries
● A Command tells the system to perform a specific business operation
○ A command may return some result
○ A command either completes successfully or fails with an error
● A Query asks the system for specific data
○ A query always returns something
○ A query only fails because of external runtime errors such as network problems
● Commands and queries can be synchronous or asynchronous
● You can model each command and each query as a separate class
Forget about services!
16. The query/command class
● The name is always a verb in its imperative form
● The parameters are class attributes
● Use a generic parameter for the result, if any
● Try to make your class immutable
○ Not always possible, e.g. if you are binding a form directly to your command
● Validate the parameters inside your class
○ If you do this in the constructor, you cannot create a command or query with invalid parameters
18. Examples (code)
public class CreatePatient implements Command<Patient> {
final String firstName;
final String lastName;
final Gender gender;
final LocalDate birthDate;
public CreatePatient(String firstName, String lastname,
Gender gender, LocalDate birthDate) {
this.firstName = Objects.requireNonNull(firstName);
…
}
}
19. Examples (code)
public class FindOrders implements Query<List<Order>> {
final LocalDate from;
final LocalDate to;
final Optional<Customer> customer;
public FindOrders(LocalDate from, LocalDate to) {
…
}
public FindOrders(LocalDate from, LocalDate to,
Customer customer) {
…
}
}
20. What about the implementation?
● Technically, queries and commands are implemented in the same way
● Each command/query has a handler
● The handler contains a single method that accepts the command/query as a
single parameter and returns the result of the command/query
● Handlers are transactional
● Similar queries/commands can share a common base class
● Handlers can invoke other handlers
21. Finding the correct handler
● A client will never invoke another handler directly
● Instead, commands/queries are passed to a broker or gateway
● The broker/gateway will look up the correct handler, invoke it and return the
result
22. Advantages
● An easy way of modeling the dynamics of the problem domain
● Forces you to think in terms of the domain while designing your UI
○ Since your UI should only be constructing and invoking commands/queries, the risk of business
logic ending up there should be smaller (I hope)
● Your business logic consists of small, highly cohesive classes
● No more god classes
● Easy to distribute, e.g. by using a service bus
○ Command and queries could be serialized into JSON
○ Command and query handlers can be running anywhere
● Easy to run asynchronously
● You can use separate data sources for reading and writing
○ CQRS
24. Disadvantages
● More boilerplate code: for each operation, you have to write a command/query
class and a handler class
● The client does not invoke the handler directly so if the handler is missing, it
will not be detected until during runtime
● You have to write the command/query framework yourself
● You can still write too coarse or too fine grained commands
26. What about long-running transactions?
● Many commands participate in the same conversation
● If your container supports it, you can make your handlers conversation scoped
○ This makes it possible to store state in the handlers
● If you want your handlers to remain stateless, create a conversation object that
is returned by and passed to every command handler
● The conversation object contains the state necessary to move the
conversation forward
27. Summary
● Discipline vs patterns
● Transaction boundary
● High cohesion
● Separate read and write
● Avoid CRUD
● Consider using commands and queries in business driven user interfaces