diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7c6507c71ec6594bb9077e62dbdf57fd93d087a7..72eb7e3a53d9d2946b4d8cf301943516b997919e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ include_directories(reading_machine/include)
 include_directories(torch_modules/include)
 include_directories(trainer/include)
 include_directories(decoder/include)
+include_directories(macaon/include)
 include_directories(utf8)
 
 add_subdirectory(fmt)
@@ -40,4 +41,5 @@ add_subdirectory(reading_machine)
 add_subdirectory(torch_modules)
 add_subdirectory(trainer)
 add_subdirectory(decoder)
+add_subdirectory(macaon)
 
diff --git a/decoder/CMakeLists.txt b/decoder/CMakeLists.txt
index 3b73f807d7a85442492cecb21c2036c04715b45f..88cee1bf44e33d317c62688c0fec0e142f155582 100644
--- a/decoder/CMakeLists.txt
+++ b/decoder/CMakeLists.txt
@@ -2,9 +2,5 @@ FILE(GLOB SOURCES src/*.cpp)
 
 add_library(decoder STATIC ${SOURCES})
 target_link_libraries(decoder reading_machine)
+target_link_libraries(decoder Boost)
 
-add_executable(macaon_decode src/macaon_decode.cpp)
-target_link_libraries(macaon_decode Boost)
-target_link_libraries(macaon_decode decoder)
-target_link_libraries(macaon_decode common)
-install(TARGETS macaon_decode DESTINATION bin)
diff --git a/decoder/include/MacaonDecode.hpp b/decoder/include/MacaonDecode.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8d314a4b9733109b602eeb60663bcfca114cb86
--- /dev/null
+++ b/decoder/include/MacaonDecode.hpp
@@ -0,0 +1,27 @@
+#ifndef MACAONDECODE__H
+#define MACAONDECODE__H
+
+#include <boost/program_options.hpp>
+
+namespace po = boost::program_options;
+
+class MacaonDecode
+{
+  private :
+
+  int argc;
+  char ** argv;
+
+  private :
+
+  po::options_description getOptionsDescription();
+  po::variables_map checkOptions(po::options_description & od);
+
+  public :
+
+  int main();
+  MacaonDecode(int argc, char ** argv);
+
+};
+
+#endif
diff --git a/decoder/src/macaon_decode.cpp b/decoder/src/MacaonDecode.cpp
similarity index 90%
rename from decoder/src/macaon_decode.cpp
rename to decoder/src/MacaonDecode.cpp
index d5dda544924b1642663865bf09438e2afa3eb488..2c42868900473cfa5ff8bcddafc5d0280568a854 100644
--- a/decoder/src/macaon_decode.cpp
+++ b/decoder/src/MacaonDecode.cpp
@@ -1,11 +1,9 @@
-#include <boost/program_options.hpp>
+#include "MacaonDecode.hpp"
 #include <filesystem>
 #include "util.hpp"
 #include "Decoder.hpp"
 
-namespace po = boost::program_options;
-
-po::options_description getOptionsDescription()
+po::options_description MacaonDecode::getOptionsDescription()
 {
   po::options_description desc("Command-Line Arguments ");
 
@@ -31,7 +29,7 @@ po::options_description getOptionsDescription()
   return desc;
 }
 
-po::variables_map checkOptions(po::options_description & od, int argc, char ** argv)
+po::variables_map MacaonDecode::checkOptions(po::options_description & od)
 {
   po::variables_map vm;
 
@@ -60,10 +58,10 @@ po::variables_map checkOptions(po::options_description & od, int argc, char ** a
   return vm;
 }
 
-int main(int argc, char * argv[])
+int MacaonDecode::main()
 {
   auto od = getOptionsDescription();
-  auto variables = checkOptions(od, argc, argv);
+  auto variables = checkOptions(od);
 
   std::filesystem::path modelPath(variables["model"].as<std::string>());
   auto machinePath = modelPath / ReadingMachine::defaultMachineFilename;
@@ -97,3 +95,7 @@ int main(int argc, char * argv[])
   return 0;
 }
 
+MacaonDecode::MacaonDecode(int argc, char ** argv) : argc(argc), argv(argv)
+{
+}
+
diff --git a/macaon/CMakeLists.txt b/macaon/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2d95abc71b9225dc91f3813e39f87cba7f52747a
--- /dev/null
+++ b/macaon/CMakeLists.txt
@@ -0,0 +1,6 @@
+FILE(GLOB SOURCES src/*.cpp)
+
+add_executable(macaon src/macaon.cpp)
+target_link_libraries(macaon trainer)
+target_link_libraries(macaon decoder)
+install(TARGETS macaon DESTINATION bin)
diff --git a/macaon/src/macaon.cpp b/macaon/src/macaon.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a80444377c9d04ecff98195015904c5b972c44cd
--- /dev/null
+++ b/macaon/src/macaon.cpp
@@ -0,0 +1,30 @@
+#include "MacaonTrain.hpp"
+#include "MacaonDecode.hpp"
+
+void printUsageAndExit(char * argv[])
+{
+  fmt::print(stderr, "USAGE : {} (train | decode)\n", argv[0]);
+  exit(1);
+}
+
+int main(int argc, char * argv[])
+{
+  if (argc < 2)
+    printUsageAndExit(argv);
+
+  if (argv[1] == std::string("train"))
+  {
+    MacaonTrain program(argc-1, argv+1);
+    return program.main();
+  }
+  else if (argv[1] == std::string("decode"))
+  {
+    MacaonDecode program(argc-1, argv+1);
+    return program.main();
+  }
+  else
+    printUsageAndExit(argv);
+
+  return 0;
+}
+
diff --git a/trainer/CMakeLists.txt b/trainer/CMakeLists.txt
index 649eae02684ce335d4f69ecf132ec8444857f729..24213b3e60814115b3a65c68d112e738e282edf8 100644
--- a/trainer/CMakeLists.txt
+++ b/trainer/CMakeLists.txt
@@ -3,10 +3,4 @@ FILE(GLOB SOURCES src/*.cpp)
 add_library(trainer STATIC ${SOURCES})
 target_link_libraries(trainer reading_machine)
 target_link_libraries(trainer torch_modules)
-
-add_executable(macaon_train src/macaon_train.cpp)
-target_link_libraries(macaon_train Boost)
-target_link_libraries(macaon_train trainer)
-target_link_libraries(macaon_train decoder)
-target_link_libraries(macaon_train common)
-install(TARGETS macaon_train DESTINATION bin)
+target_link_libraries(trainer Boost)
diff --git a/trainer/include/MacaonTrain.hpp b/trainer/include/MacaonTrain.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad00e9deeececc2652c4df0d92fc5f538f1b86fd
--- /dev/null
+++ b/trainer/include/MacaonTrain.hpp
@@ -0,0 +1,30 @@
+#ifndef MACAONTRAIN__H
+#define MACAONTRAIN__H
+
+#include <boost/program_options.hpp>
+#include "Trainer.hpp"
+#include "Decoder.hpp"
+
+namespace po = boost::program_options;
+
+class MacaonTrain
+{
+
+  private :
+
+  int argc;
+  char ** argv;
+
+  private :
+
+  po::options_description getOptionsDescription();
+  po::variables_map checkOptions(po::options_description & od);
+  void fillDicts(ReadingMachine & rm, const Config & config);
+
+  public :
+
+  MacaonTrain(int argc, char ** argv);
+  int main();
+};
+
+#endif
diff --git a/trainer/src/macaon_train.cpp b/trainer/src/MacaonTrain.cpp
similarity index 93%
rename from trainer/src/macaon_train.cpp
rename to trainer/src/MacaonTrain.cpp
index 2b378eba8157ce84c11daf0f3939170783bf554b..3df1365266758b5044b5b65db86221a807b05dbf 100644
--- a/trainer/src/macaon_train.cpp
+++ b/trainer/src/MacaonTrain.cpp
@@ -1,13 +1,11 @@
-#include <boost/program_options.hpp>
+#include "MacaonTrain.hpp"
 #include <filesystem>
 #include "util.hpp"
-#include "Trainer.hpp"
-#include "Decoder.hpp"
 #include "NeuralNetwork.hpp"
 
 namespace po = boost::program_options;
 
-po::options_description getOptionsDescription()
+po::options_description MacaonTrain::getOptionsDescription()
 {
   po::options_description desc("Command-Line Arguments ");
 
@@ -40,7 +38,7 @@ po::options_description getOptionsDescription()
   return desc;
 }
 
-po::variables_map checkOptions(po::options_description & od, int argc, char ** argv)
+po::variables_map MacaonTrain::checkOptions(po::options_description & od)
 {
   po::variables_map vm;
 
@@ -61,7 +59,7 @@ po::variables_map checkOptions(po::options_description & od, int argc, char ** a
   return vm;
 }
 
-void fillDicts(ReadingMachine & rm, const Config & config)
+void MacaonTrain::fillDicts(ReadingMachine & rm, const Config & config)
 {
   static std::vector<std::string> interestingColumns{"FORM", "LEMMA"};
 
@@ -77,10 +75,10 @@ void fillDicts(ReadingMachine & rm, const Config & config)
       }
 }
 
-int main(int argc, char * argv[])
+int MacaonTrain::main()
 {
   auto od = getOptionsDescription();
-  auto variables = checkOptions(od, argc, argv);
+  auto variables = checkOptions(od);
 
   std::filesystem::path modelPath(variables["model"].as<std::string>());
   auto machinePath = modelPath / "machine.rm";
@@ -173,3 +171,7 @@ int main(int argc, char * argv[])
   return 0;
 }
 
+MacaonTrain::MacaonTrain(int argc, char ** argv) : argc(argc), argv(argv)
+{
+}
+