Upload
petter-holmstroem
View
128
Download
0
Embed Size (px)
Citation preview
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
Disclaimer:Business logic creeping into the UI is not really an architectural problem but a problem with developer discipline
Let’s look at a “classic” architecture
Persistence layer (JPA)
Service layer (EJB)
UI layer (Vaadin)
Entities
SQL database
Problem 1
UI Operation
Service 1
Service 2
Service 3
Actual transactionboundaries
Logical transaction boundary
Problem 2
UI CRUD Service+
Business logic in the user’s head and in the UI
Service layer is only a frontend to the database
Problem 3
UI 1
UI 2
UI 3
God Service
Problem 4
Service A Service B
New method
?
What can we do?
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
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
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
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
Introducing a Command based architecture
● 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!
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
Examples
<<command>>CreatePatient<Patient>
firstName: string
lastName: string
gender: Gender
birthDate: LocalDate
<<query>>FindOrders<List<Order>>
from: LocalDate
to: LocalDate
customer: Optional<Customer>
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);
… }
}
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) {
… }
}
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
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
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
Master SlaveSlave
Query Handler Query HandlerCommand Handler
Broker
UI
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
Time for a code example!
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
Summary
● Discipline vs patterns● Transaction boundary● High cohesion● Separate read and write● Avoid CRUD● Consider using commands and queries in business driven user interfaces
That’s all folks!