Introduction
JavaFx is a new rich client framework by Oracle. It allows you to create visually pleasing enterprise business applications with the Java language.While many small demos are showing up in the community, there aren't many articles on how to structure your JavaFx application. Using a good structure makes it easier to maintain your application, add new features and improves overall stability. These are all very important in enterprise applications.
This serie of articles will try to remedy that by showing you one of many ways to structure your application. This is certainly not the only way to do it, but it will give you a starting point. Even this structure is open for improvement (as every structure is).
This serie will consist of four blog posts:
- Overview (this blog post)
- View layer
- Application logic layer
- Service - Application State layer
High-level Overview
The structure I will describe is based on Robotlegs, an Apache Flex micro-architecture library. But instead of using events to decouple the layers, I went with for direct calls (with interfaces in-between). While events allow you to decouple the layers even more, I prefer direct calls with interfaces over events because they make it easier to follow the flow of your application.The diagram below shows the different components and layers in my architecture. The arrows indicate the dependencies.
The diagram below shows a sample flow of an application. In this case the user clicked on a button to delete a contact in a list.
- The view asks the mediator to delete the item by calling the deleteContact(contact) method on the IContactListMediator interface.
- The mediator creates a new DeleteContactCommand and registers a success handler on it. Then it calls the start() method on the command.
- The command calls the contact service to delete the contact from the database
- The command calls a method on the model to remove the contact from the application state. It receives the updated list of contacts from the model
- The command finishes and triggers the registered success listeners. It provides the updated list of contacts to the success handlers.
- The success handler in the mediator asks the view to update the list of contacts based on the updated contact list it got from the command.
Used libraries
For my application I didn't use any framework (except for JavaFx, obviously), I wanted to create my own structure without the overhead of a framework. All I used was the JavaFx framework and Guice for dependency injection.For those interested in build tools, I used the JavaFx Gradle plugin created by Danno Ferrin to create a build script.
Dependency Injection
Mapping your dependencies
A DI library like Guice allows you to inject instances of certain classes into other classes without you having to manually instantiate and inject them. This can greatly improve the quality of your code since you don't have to write all that yourself. Guice is basically the glue that holds your application together.In Guice you have to create a Module class which defines all the mappings. Such a module class can look like this for a simple application:
public class EventManagerModule extends AbstractModule { @Override protected void configure() { bind(EventManagerModule.class).toInstance(this); mapViews(); mapMediators(); mapCommands(); mapServices(); mapModels(); mapInfrastructure(); } private void mapViews() { bind(IEventOverviewView.class).to(EventOverviewView.class); bind(IEventListView.class).to(EventListView.class); bind(IEventDetailView.class).to(EventDetailView.class); } private void mapMediators() { bind(IEventOverviewMediator.class).to(EventOverviewMediator.class); bind(IEventListMediator.class).to(EventListMediator.class); bind(IEventDetailMediator.class).to(EventDetailMediator.class); } private void mapCommands() { bind(ICommandProvider.class).to(CommandProvider); bind(LoadEventsCommand.class); bind(AcceptEventCommand.class); bind(DeclineEventCommand.class); } private void mapServices() { bind(IEventService.class).to(StubEventService.class); } private void mapModels() { bind(IEventSelectionModel.class).to(EventSelectionModel.class).in(Singleton.class); } private void mapInfrastructure() { bind(ITranslationProvider.class).to(ResourceBundleTranslationProvider.class).in(Singleton.class); } }So in most cases you will be mapping implementations to interfaces. Always try to use interfaces. This allows you to, for example, create a stub implementation of a service (in this case StubEventService) and a real implementation which you can then just swap by changing the Guice mapping.
Only for the Commands I didn't create interfaces because they wouldn't add that much value. They would all practically be marker services, but you'll see that in my following posts.
While this Module implementation is ok for a small application, it can quickly explode in one huge class when you're building a bigger application. This is where bootstrap classes come in handy. A bootstrap class is just a class which initializes a certain part of your application. In this case it would initialize the mappings of the views, the mediators or the commands. Such a bootstrap class would look like this:
public class MediatorMapper implements Mapper { private AbstractModule module; public MediatorMapper(AbstractModule module) { this.module = module; } public void bootstrap() { module.bind(IEventOverviewMediator.class).to(EventOverviewMediator.class); module.bind(IEventListMediator.class).to(EventListMediator.class); module.bind(IEventDetailMediator.class).to(EventDetailMediator.class); } }Your module would then look like this:
public class EventManagerModule extends AbstractModule { @Override protected void configure() { bind(EventManagerModule.class).toInstance(this) new ViewMapper(this).bootstrap(); new MediatorMapper(this).bootstrap(); new CommandMapper(this).bootstrap(); new ServiceMapper(this).bootstrap(); new ModelMapper(this).bootstrap(); new ModelMapper(this).bootstrap(); } }
Wiring it all together
Now how do you wire all this together? Well, that is what I'm going to show you now.In your Main class you will create a Guice Injector and with this injector you will instantiate the main view of your application. Your Main class will look like this:
public class Main extends Application { public static void main(String[] args) { Application.launch(); } @Override public void start(Stage stage) throws Exception { Injector injector = Guice.createInjector(new EventManagerModule()); EventOverviewView eventOverviewView = injector.getInstance(EventOverviewView.class); Scene scene = SceneBuilder.create() .root( eventOverviewView.getView() ).width(900) .height(700) .build(); scene.getStylesheets().add(Main.class.getResource("/EventManager.css").toExternalForm()); stage.setScene(scene); stage.show(); } }The views that are being used in the main view are also injected in the main view. Also, the mediator that is behind the main view is also injected. This patterns is used for every view.
public class EventOverviewView implements IEventOverviewView { @Inject public IEventOverviewMediator mediator; @Inject public EventListView eventListView; @Inject public EventDetailView eventDetailView; ... }Then, in the mediator, we inject the view and the commandProvider (this is a class which allows to create command instances on the fly).
public class EventOverviewMediator implements IEventOverviewMediator { @Inject public IEventOverviewView view; @Inject public CommandProvider commandProvider; ... }And in the application logic layer we inject the service/model in the command.
public class LoadEventsCommand extends Service<List<EventVO>> { @Inject public IEventService eventService; ... }
Conclusion
In this post I gave a broad overview of the structure and explained how dependency injection fits into this.In the following posts I will go into more detail for each layer and give some best practices.
You can find the full sample application here: https://github.com/SlevinBE/JavaFx-structure-demo
Glad to see this. Cool approach too. Best practices for structuring JavaFX apps really haven't been established yet, so it is good to see some posts on the topic emerging.
ReplyDeleteThe approach is too layered for my taste; I'd like to keep it simpel.
ReplyDeleteGiven that the view is defined in a FXML file (or setup manually), then the mediator would be a controller or backing bean. If the "delete contact" button is pressed, for me, the backing bean should do a (skipping null checks):
em.find(Contact.class, 123).remove();
And on success also remove the contract from the list in the backing bean populating the JavaFX ListView control (or maybe do a JPA query to repopulate).
If you want to go enterpricy and move the whole business model onto its own application server, then the backing bean talks to that API directly. Say a REST interface:
http[DELETE]://appserver/contact/123
I'd drop the command layer completely.
If you would do all that in your backing bean then it can become a rather big class, which makes it harder to unit test (more dependencies, more code, ...).
ReplyDeleteAlso, you would have to make sure that in your backing bean all the logic/remote calls that can take some time are executed in a separate thread to not block the UI, which further clutters your code. In my approach this is nicely separated in the command layer.
We can KISS(Keep it simple , stupid) by making use of FXML - Controller class and simple methods that makes the CRUD operations.
ReplyDeleteMaking sure the UI stays responsive is indeed the responsibility of the backing bean; it knows when it can simply wait or put stuff in a separate thread (and provide a feedback on progress). Of course some utility code to prevent scaffolding is needed, but it's all part of the UI layer. Naturally there should be no business logic in the backing bean, just the code connecting the UI to the model. Any business logic is part of the rich domain model.
ReplyDeleteAbout the class growing; kinda depends on the JFX screen, but with loads of controls the backing bean can become bigger, yes. Hard to avoid. The per-control code still remains constrained to max 5 lines or so. On the other hand, a pile of interfaces don't make things more readable either.
@tbee I think we both describe a valid architecture which can be used depending on the requirements of the application. Correct me if I'm wrong, but it looks like you are describing an architecture for a thin client (where the business logic is located on the server), while my architecture is for fat clients (business logic in the client).
DeleteSo in my case the client will be more complex (therefore the command layer), while your solution doesn't need that because the business logic is on the server.
I also thought about best practices for the structure of my JavaFX apps. I fully agree with Ajay V. that the use of FXML Controller classes is (in many cases) a good solution when separating application logic from actual UI elements.
ReplyDeleteI created a small framework to ease the integration of Guice into your JavaFX applications a while ago (It's called: "fx-guice").
--> https://github.com/cathive/fx-guice
By extending "GuiceApplication" instead of "javafx.app.Application" you gain a bunch of advantages:
* You can @Inject your app object anywhere as it will be
automatically bound
* You can use the GuiceFXMLLoader to load FXML files and use both
@FXML and @Inject annotations in your FXML Controller classes
* You can use @FxApplicatioNThread to mark methods that must be
executed on the JavaFX event dispatch thread
* ...
"fx-guice" is published under the Apache License v2.
Source code can be found on Github and ready-to-use artifacts can be found in the Sonatype OSS Maven repository. I also uploaded some (very simple) example apps (e.g. a cute little calculator) that shows how to use my framework.
--> https://github.com/cathive/fx-guice/tree/master/examples/calculator-example-app
I feel that writing the common boiler plate code over and over again is kind of boring and it's really time for a proper Guice extension to deal with JavaFX integration. :-)
Cool! I'll certainly have a look at this. Something which nicely wires up FXML Controller classes with Guice was certainly missing up until now.
DeleteI'm looking for good information on IoC and presentation model, I hit a robot legs guy!
ReplyDeleteI actually ran into Benjamin's library today ironically and am going to be using it in a couple of my desktop apps, it looks well written and has a lot of possibilities.
I still have a ways to go to wrap my head around JavaFx, been into it 2 days now but, it looks very powerful for what I need.
Funny enough and Apache Flex comitter, working on an AS3 -> JS cross compiler with Falcon.
I will be reading your blog.
PS I don't write any Flex code these days, but for some reason still love AS3.
It's a small world ;)
DeleteJavaFx is indeed very powerful (dare I say even more powerful than Flex). It has threads, the JVM languages (Java, Groovy, Scala,...), a build in Web engine (based on WebKit) and is imo architected better than Flex.
I've been following the Apache Flex mailing list, and you're doing some great work on the AS3 -> JS part!
Yeah, just wait to see what is going to be released next week by Mike Laboriola and the gang. (IE Joel Hooks might might be floating around this project as well).
ReplyDeleteIts something very fun with AS -> JS that you might actually think is quite interesting based on your interests of these types of design patterns.
I have Caustic audio framework;
http://www.teotigraphix.org/causticcore/overview/
That is what I "do", writing a cross compiler is for run. :) So I am making desktop applications to manager mobile audio apps, JavaFX has really blown me away, I thought I was going to have to go back to 2002 and use Swing! :)
Mike
I really like the way you structure the code. It's very clean, testable and easy to maintain. Besides the possibility of using some other posted libraries - I got a simple question.
ReplyDeleteI've taken a look at the github repository - you're defining VOs (I guess View-Objects). How can this work together with persistent frameworks. I can't imagine any other possibility then converting the given VO to a POJO or direct sql - which both adds a lot of unnecessary code.
What's your oppinion on this?
The sample was made for a client-server architecture, so the client won't persist anything, that's the server's responsibility.
DeleteSo the server and client use VO objects to transfer data. The server will convert (e.g. Hibernate) mapped POJO's to VO and vise versa.
This does indeed lead to a lot of extra code (in the form of mapper classes), but this is where mapper libraries like Dozer (http://dozer.sourceforge.net/documentation/gettingstarted.html) can help with.
It also has a couple of important advantages:
1. you can't get lazy initialization exceptions.
2. The view only gets the information it needs and in the format it wants to use it.
But if you have a simple application which needs to persist something to the database which doesn't necessarily requires a server, then feel free to replace those VO's with mapped POJO's and create a service in your client which persists these.
So in the end it all depends on the type of application you want to build. For example this architecture is a fat client architecture, but if you want to keep your business logic on the server (thin client architecture) there may be better client architectures.
Hi,
ReplyDeleteThanx for the interesting post/topic (which is shamely rarely discussed by tutorials as you noticed).
If I understand all of this correctly, your main entry point in the stack/structure of the application comes from the top (the view) :
«
Injector injector = Guice.createInjector(new EventManagerModule());
EventOverviewView eventOverviewView = injector.getInstance(EventOverviewView.class);
»
Why doesnt the responsibility for entering the stack (initing the commands / creating the main view, etc.) doesnt come from the Mediator/Orchestrator/Controller/Whatever ?
Because this was the most straightforward way to do it, I didn't want to make the initialization process more complex than it should be.
DeleteIf you look at it from a purely architectural/theoretical viewpoint you are indeed correct that this could be better initialized somewhere else. However, sometimes I weigh the cleanest solution to the amount of complexity it adds, and in this case I thought it wasn't worth it.
Every JavaFx developer recognizes a Main class (or any class that has a main method and which extends Application) as the entry point of a JavaFx application. That's also why I do it in there because in most cases this class won't contain all that much code.
Awesome stuff, i used Robotlegs in tons of Projects and was looking for a similar aproach in JavaFX. Thats perfect. Not quite the same without the event dispatching stuff, but close.
ReplyDeleteI also like building components in Flex better then JavaFX, but overall, its pretty much the same stuff.
Still, love Flex and always will :D
Broken link to: structure-flow.png
ReplyDeleteI can see the image and open it in a new tab (even after clearing my browser cache).
Deletehttp://dl.bintray.com/content/shemnon/javafx-gradle/javafx.plugin asks for user+pass
ReplyDeleteFixed. I updated the url to the JavaFx gradle plugin in the build file.
DeleteIt provide a simple but powerful pattern which will help you to structure your application.
ReplyDeleteThin Client Hardware & Zero Client
This Very Nice and attractive Article…
ReplyDeleteThank you For Sharing This Information…
And also give to me important knowledge.
Desktop Application Development India
iPhone App Development Company
ReplyDeleteThis is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.
I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
Java training in Indira nagar
Java training in Rajaji nagar
Java training in Marathahalli
Java training in Btm layout
Java training in Marathahalli
This Very Nice and attractive Article…
ReplyDeleteThank you For Sharing This Information…
And also give to me important knowledge.salesforce training in chennai
software testing training in chennai
robotic process automation rpa training in chennai
blockchain training in chennai
devops training in chennai
nft nasıl alınır
ReplyDeleteen son çıkan perde modelleri
minecraft premium
özel ambulans
en son çıkan perde modelleri
uc satın al
lisans satın al
yurtdışı kargo
Good content. You write beautiful things.
ReplyDeletevbet
mrbahis
mrbahis
korsan taksi
taksi
hacklink
sportsbet
hacklink
sportsbet
çorum
ReplyDeleteantep
ısparta
hatay
mersin
J61TY
salt likit
ReplyDeletesalt likit
dr mood likit
big boss likit
dl likit
dark likit
P8İRM8
عوازل اسطح
ReplyDeleteكوله