As Josh K mentioned, you can create a Window Group for opening one or more windows with specific layouts and appearances. In addition, "settings profiles" (Terminal > Preferences > Settings) can specify a command to run when a terminal is created with that profile. You can create a separate profile for each command you wish to run, then create windows with those profiles. When you save them to a window group and open them again, those commands will run in their appropriate windows. You can even have Terminal open a specific window group at startup.
As of Mac OS X Lion 10.7, there are several new improvements in this area:
Resume will automatically restore windows that were open when you quit Terminal, so using a window group for this is no longer strictly necessary (although they allow you to re-create a specific setup if you've closed the windows).
If you create a new terminal using Shell > New Command, since Terminal knows what command you ran, it can restore it by running it again. Terminal will now automatically restore "safe" commands† when Resuming.
Terminal will also restore these commands if you save these kinds of terminals into a window group. In addition, when saving a window group, there's an option to restore all commands, not just the "safe" ones. So, you no longer need to create a separate settings profile for each command you want to run.
† "Safe" commands include anything listed in /etc/shells
, emacs, vi/vim, nano, pico, top, screen, tmux, and any command created with Shell > New Remote Connection (e.g., ssh sessions). Note that only top is considered "safe" when used with arguments; the others support arguments that may do things you wouldn't want them to without user confirmation, so they are not considered "safe" for automatic restoration unless they are invoked without arguments. You can customize the list of "safe" commands with "defaults write com.apple.Terminal RestorableCommands". The value is a list of strings containing the command names. To indicate that arguments are allowed, add an asterisk after a command. e.g., "top *" makes top safe when used with arguments, but "top" does not.