Every minigame I make are very similar internally.
Each minigame has a Game class, which has a game loop and methods such as addPlayer(), removePlayer(), startGame(), etc. Minigames normally have the following:
Game loop: This is called every second. It's used to track things like the countdown, or even checking if the game still has enough players to keep playing.
Add or Remove player methods: These are used when you want to add or kick a player from the game. When you add the player, it saves the player's inventory, location, exp, hunger, and health to be restored once the game has ended. It then clears his inventory, then adds any items needed (armor, weapons, etc). It then uses the sendMessage method to notify users ingame that a player has joined. This method is normally called when the player sends the join command, or when they interact with a join sign.
Send Message: This one is simple. In fact, it's only 3 lines. It takes a String as an argument, and sends a message with a specially colored tag with the name of the game to all players along with the String argument.
End and Start game: These methods control when the game starts or ends. The Game Status is an enum, typically GameStatus.WAITING or GameStatus.INGAME. These are used with listeners. For example, if I don't want a player to be able to move unless the game has already started, I could just cancel his movements if the game he is in has a status of WAITING. The Start Game method sends a chat message to all players, and sometimes executes a bit of code when deciding teams or giving special items depending on the team. A good example of this is when I made my Infection minigame. Two out of all the players are selected to be infected when the game starts. It's also then that they are given items depending on whether they are infected or not. The end game method is used when the game has ended (not enough players, or all players eliminated, etc) or when the server is stopped. This one gives back the player data saved during the AddPlayer method and resets the countdown timer and GameStatus.
As well as the Game class, I also have the GameManager. This contains some useful functions, such as getGame(ID). When a new Game instance is created, it is registered with the GameManager. When registered, it goes into a list of games. This list is accessed by the Main class, which then calls a doSecond() method in every Game instance. The GameManager also has functions for getting whether a player is in a Game, and even getting the Game a player is in.