These are the trainer's statements and guidelines - as I recall and understood them, and completely out of context.
Web applications
- Always front the JavaEE container with a fronting HTTPS web server (for example Undertow or the Apache httpd server) which should host all static files and act as a reverse proxy forwarding requests to dynamic resources to the JavaEE container.
- Do not set up SSL on the JavaEE container. Set it up on the fronting web server.
- Go for a JavaScript framework.
- JavaScript VMs/engines are nowadays incredibly fast.
- The whole world is already there.
- Coming from a Java developer experience, use TypeScript instead of JavaScript.
- My conclusion: With JSF, use BeanValidation (JSR 303) instead of JSF validators.
- JSF validators depend on the component, while data validation has nothing to do with the page structure/component hierarchy. There is no reason to couple validation of incoming data with JSF page's components.
- There is no reason to submit to the server data that has not been validated before by JavaScript in the browser.
- This is not how JSF works. This is a reason why we should avoid using JSF.
- Addition by myself: Validation by JavaScript does not mean that the server can trust the incoming data. The server should always validate incoming data.
- JSF will eventually die, the question is when.
- During the next 5 years?
- Use ReactJS
- It is backed and actively used by Facebook
- AngularJS is backed by Google, but it is not really actively used by Google.
Web services & integration
- Use REST
- Use JSON Web Tokens for request authentication
- Use Protocol Buffers for REST (instead of JSON) & JMS
- Avoid distributed transactions, rather use compensation instead, as in real life.
- The concept of transaction mandates that everything inside the transaction happens at the very same instant, while it is actually not the case.
- With REST services, use a HTTP POST if you don't know the ID of the generated resource.
- Use Spring REST instead of JAX-RS and RestEasy.
- Test REST services using httpie.org
- JAX-RS creates, by default, 1 resource class instance per request.
- This does not scale (instantiation of objects is slow, and GC time increases, which consumes too much resources).
- Instead, implement the @Override getSingletons() operation
- With JAX-RS, do not use class attributes to get request headers, cookie data etc. This would imply that the one-request-per-instance strategy is needed, which does not scale well.
- Use Jackson to do JSON binding
- With JAX-RS, prefer to return Response objects.
- Use Akka-HTTP to implement REST services.
- There are both a Scala and a Java API.
- This is a non-blocking API, which thus scales well (threads are not blocked waiting).
- This REST API has a client-side API as well
- For security of REST services, using BASIC auth + SSL (+ JSON web tokens) is OK
- Both Spring REST and Akka HTTP support this out of the box.
- MOM (message oriented middleware) is the de-facto standard for inter-system communication in large organisations.
- You should not use a big single ESB anymore
- Use asynchronous JMS clients
- Use Apache Camel
CDI & EJB
- Avoid using @Producer
- Use producers when you need to create instances of classes of libraries/frameworks which come with their own factories: the @Producer method would thus just be a facade for these specific factories
- Your own beans are automatically instantiated by the CDI container anyway, provided they are marked with a bean-identifying annotation.
- Do not produce primitive (int, boolean...) or String values with @Producer, rather set the values as static variables or access these values using static methods
- Use EJBs. There is no reason to avoid EJBs in favour of CDI-only solutions (for transactionality and security, like provided by Apache DeltaSpike).
- Apache DeltaSpike is not mainstream, and is not supported. Be cautious about using it.
- Inject required attributes and objects directly into the constructor, instead of using setters or attribute access.
- If a required attribute is not available, construction should fail and the object should not be usable anyway
- There is no need to put an interface on stateless session beans or Spring beans. Stateless session beans and Spring beans needed interfaces in the past because the generation of proxies without interface was not possible. Nowadays, with javassist and cglib, this is no more the case. The architectural benefit of putting an interface on a stateless session bean or a Spring bean has never really been used anyway.
- You mostly want to use @Singleton beans.
- By default, access to any method of a singleton bean is write-locked. There is one write lock per bean instance. As there is only one singleton bean instance, there is one single write lock for all method calls, which means that methods are called sequentially, one at a time, which has a dramatic impact on performance.
- Use @Singleton with @Lock(LockType.READ) for better performance.
- In Session Beans, any system exception is wrapped by an EJBException by the container.
- The EJB scheduling is quite poor compared to other scheduling APIs or frameworks. Prefer using Quartz instead of EJB scheduling.
- Quartz is anyway the de-facto standard in Java programming world.
Load-balancing, clustering & multiple JavaEE container instances
- To store shared transient (non persistent, like session attributes) state, use the client's browser storage instead of distributed replicated session mechanisms.
- Use load-balancing and stateless services
- Avoid stateful EJBs
Wildfly & JBoss EAP
- Put custom modules directly into the module folder of the Wildfly distribution, as siblings of the module/system folder. Do never modify anything inside the modules/system folder.
- A JBoss modules slot specifies a version of a module.
- Do never work or modify with the original standalone or domain folder of your JBoss EAP/Wildfly distribution. Always use a copy.
- Deploy DB drivers as JBoss modules, then register the driver into the container by referencing the corresponding module, then define your datasources using the registered driver.
IDE
- Use Jetbrain's IntelliJ IDEA Ultimate edition.
- Forget Eclipse.
- During development, always deploy exploded archives.
- And in IntelliJ, enable JavaScript debugging.
- This requires the installation of a Google Chrome plugin.
Testing
- Unit tests do not relate to a method or to a class, but to a component. A component may be composed of multiple encapsulated classes.
- The unit of test is the component.
- What is tested is the behaviour of the component, not its internals.
- If we test the internals of a component, like for example each class, we would end up with too many mocks and too many tests to maintain, while these would be easy to break (any implementation refactoring would imply to maintain the tests and mocks).
- We should test our components.
- Dependencies to other components (usually at most a few per component to test) should be mocked.
- Read Martin Fowler's Is TDD dead article
- Test-first is not TDD.
- Test-first is nice in theory. But it fails in practice. Do not apply it.
- Use gatling.io for performance tests
- Use Selenium +Arquillian Drone
- Do component unit test + end-to-end tests.
- If you cannot do end-to-end tests, then only you should do integration tests.
- Read the testing book with a tulip on the cover
- The trainer mentioned BDD and cucumber.io
- Use the specs2 Scala library to implement tests
Development practices
- Use Docker from DEV to PROD.
- The whole world is going down that road.
- Always favour strong typing instead of using String.
- Read Eric Evan's Domain Driven Design book to understand what a domain model really is.
- Do not use magic numbers (error codes or HTTP status codes, for example)
- Use Scala instead of Java
- Smoothly introduce Scala by first using it for unit tests, then for actual production code.
- Use SQL. It is a decent DSL (domain specific language) for relational data access that has lived and not been replaced for decades.
- Understand:
- Reentrancy
- Thread-safety
- The Java volatile keyword
- Do not used shared mutable state. It has to be managed (threads), and at a point there are just too many threads to manage: this thread management will eventually outweigh the advantage of adding a new thread. This solution does not scale.
- Use functional programming paradigms instead.
- Functional programming has no side-effects, and uses immutable non-shared states.
- Go to functional programming: learn Scala if you come from Java development (Haskell for hard-core functional programming)
- Use the Functional Java library.
- Checked exceptions are bad
- Throwing an exception is like a GOTO to somewhere down the stack.
- Put exceptional results into return values instead.
- Use Optional<> or Either<> return values.
- Don't throw the exception, put it into the return value.
- System/unchecked exceptions (RuntimeException) is OK, they indicate that an error occurred and the system cannot continue.
- NULL is bad
- Cf. Tony Hoare's billion dollar mistake
- Use Optional<>/Maybe<> or Either<> instead.
- Stop returning null.
- Stop accepting null from an operation. Wrap results of operations that return null into Optional<>/Maybe<>.
- In Java, Object is the Ur-/top-type: all classes extend Object, and similarly, null is a bottom-type: it inherits from all classes.
- But its behaviour does not inherit from these classes: the JVM behaves as if the implementation of any operation called on a null instance throws NullPointerException. Operations that return a object may return null, in which case what you receive is not really an instance of the promised class - it is, but it does not behave like one. This is a major flaw in the design of the Java language, which is thus not quite "honest" about how an operation can behave.
- Avoid distributed transactions.
- They imply additional latency
- They do not scale well.
- For info: The XA protocol is one implementation of the two-phase commit protocol.
- Do not force your service consumers to manage your versions.
- Your server should manage as much as possible different versions without impacting the consumers.
- Do not put version numbers in your URIs.
- In case of incompatible changes and a consumer sends a deprecated request format, just return an error message/exception.
- In the real world, there are no versions: imagine that you fill in a form for some administration. If you provided an older version of the form that is not supported anymore, the administration will tell you to fill the newest version of the form. Implement a similar behaviour in your services.
- Use reactive streams (Akka streams, for example)
- Use random generated identifiers
Persistence
- Do not use JPA if performance is important.
- Do not use JPA if you use an anemic domain model (and transaction script services).
- JPA is an ORM, but we don't really use object orientation (and the powerful encapsulation that comes with it), thus there is no need for an ORM and all its complex magic. I wrote an article about it, with more details.
- In Luxembourg, Clearstream and BGL use MyBatis.
- JPMorgan switched from Hibernate to Mybatis.
- Use Spring JDBC or MyBatis or ScalaJDBC, but not ORMs.
- Do not use triggers or stored procedures. Remove all the ones you have.
Optimistic locking
There are usually 3 ways to implement optimistic locking:- UPDATE... WHERE {all attributes have the previous value}
- If any fails, then it means that the object has been updated already
- UPDATE... WHERE {all changed attributes have the previous value}
- If any fails, then it means that the object has been updated already - more efficient, but does this really work? Other fields may have been updated as well, and this strategy would not detect it.
- UPDATE... WHERE VERSION=previous version
DevOps
- DevOps of a system is done by a single team, which is responsible for both DEV and PROD, as well as for the build and deployment.
Software architecture
- Software architects work for the company, not for the project. In fact, many projects are define because of the architects, not because of the business.
- Architectural policies apply between components
- They define how components are integrated into a bigger system
- Design policies apply to the internals of a component
- They depend on the actual use cases and requirements for the component.
- Evolution:
- 20 years ago: Component Based Design (CBD)
- 10 years ago: SOA
- Now: microservice architecture (MSA)
- At most one port (as in UML) per component
- Read the book Microservices Architecture
Miscellaneous
- Watch Halt and Catch Fire
- Watch BoJack Horseman
- Learn about set theory, group theory and finally category theory (mathematics)
- Read the Monads are elephants article series:
- http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html
- http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-2.html
- http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-3.html
- http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-4.html
- Watch the movie Clockwise (with John Cleese)
- Recurring pattern:
- If a problem occurs one time, it is one occurrence of the problem;
- If it occurs twice, it is a coincidence;
- If it occurs more often, it is a recurring pattern.
- We measure coupling to another system by counting the number of assumptions that we have to do to use the system.
- According to the trainer, there are, for Java language programming only 2 mainstream platforms: JavaEE and Spring.