Often, an effective way to hold down complexity is to break an application into a client/server pair, or a pair of peers, communicating via an application protocol. This kind of partitioning is particularly effective in situations where multiple instances of the application must manage access to resources that are shared among all instances of the application. A single server process may manage all resource contention, or cooperating peers may each take responsibility for some critical resource.
This kind of partitioning can also help distribute cycle-hungry applications across multiple hosts, and/or make them suitable for distributed computing across the Internet.
We'll discuss the related CLI server pattern in Chapter 11 (Interfaces).
PostgreSQL is an open-source database program. Had it been implemented as a monster monolith, it would be a single program with an interactive interface that manipulates database files on disk directly. Interface would be welded together with implementation, and two instances of the program attempting to manipulate the same database at the same time would have serious contention and locking issues.
Instead, the PostgreSQL suite includes a server called postmaster and at least three client applications. One postmaster server process per machine runs in background and has exclusive access to the database files. It accepts requests in the SQL query minilanguage via TCP/IP connections, and returns answers in a textual format as well. When the user runs a PostgreSQL client, that client opens a session to postmaster and does SQL transactions with it. The server can handle several client sessions at once, and sequences requests so that they don't step on each other.
Because the front end and back end are separate, the server doesn't need to know anything except how to interpret SQL requests from a client and send SQL reports back to it. The clients, on the other hand, don't need to know anything about how the database is stored. Clients can be specialized for different needs and have different user interfaces.
This organization is very typical for Unix databases — so much so that it is often possible to mix and match SQL clients and SQL servers. The interoperability issues are the SQL server's TCP/IP port number, and whether client and server support the same dialect of SQL.
In Chapter 6 (Transparency), we introduced Freeciv as an example of transparent data formats. But more critical to the way it supports multi-player gaming is the client/server partitioning of the code.
The state of a running Freeciv game is maintained by a server process, the game engine. Players run GUI clients which exchange information and commands with the server via a packet protocol. All game logic is handled in the server. The details of GUI are handled in the client; different clients support different interface styles.
This is a very typical organization for a multi-player online game. The packet protocol uses TCP/IP as a transport, so one server can handle clients running on different Internet hosts. Other games that are more like real-time simulations (notably first-person shooters) use raw Internet datagram protocol (UDP) and trade lower latency for some uncertainty about whether any given packet will be delivered. In such games, users tend to be issuing control actions continuously, so sporadic dropouts are tolerable, but lag is fatal.