Well, you mention 3-layer architecture, so I guess you're looking at a Data/Application/Presentation approach. Of course, in order for that to make sense you may need to provide more than the brief details you gave in your question.
For instance, when we talk about the Application tier, it really makes sense to have one if you have "Appalication logic". With your brief info there isn't really application logic other than displaying your data to screen. See this wikipedia entry for more info on the topic of Multitier (or n-tier) architecture (and 3-tiers as a subset) in general.
That being said, if you have your 3 tables in a data storage of sort (such as a database), we can quickly make a 3-tiers app like this.
1~ Data Tier:
Create classes that match the storage tables, such as (using C# syntax):
public class DT_UserCar
{
public string userId;
public string carId;
public string carMakeId;
public string CarModelId;
}
I'm using the DT_ prefix to indicate this class belongs to the Data Tier.
In addition, you need to have some code to let instance of these classes be read from the storage and probably be saved to storage. Of course you have options already. You could create a separate class that knows how to do all that, like
public class Storage
{
public DT_UserCar ReadUserCar(string carId) { /* implementation */ }
public DT_CarMake ReadCarMake(string carmakeId) { /* implementation */ }
/* and so on... */
}
Or you could decide that each class should know how to serialize/deserialize itself to/from the storage, and go with:
public class DT_UserCar
{
public string userId;
public string carId;
public string carMakeId;
public string CarModelId;
public static DT_UserCar Read(string carId) { /* implementation */ }
public void Write() { /* implementation */ }
}
A third, and much better alternative (for bigger projects) is to find a third-party tool that takes care of all of this for you. After all, given the storage structure (e.g.: the database schema) all of this code can be automated... I won't go into details here since you can find a lot of information about this sort of tools (ORM tools) and their characteristics, but mostly because it doesn't seem ot be part of your exercise.
2~ Application Tier:
As I said, your use case doesn't seem to include a lot of 'business logic'. However, you do mention that the data from those 3 storage tables should be merged somehow, so I'll take that as your one piece of business logic. Hence, we should create a business class (or business entity, or Domain Entity, or Domain model, whichever term you feel like using, they all have different connotations but a lot in common) like this:
public class AT_UserCar
{
public DT_UserCar _userCar;
public DT_CarMake _carMake;
public DT_CarModel _carModel;
public AT_UserCar(DT_UserCar userCar, DT_CarMake carMake, DT_CarModel carModel)
{
_userCar = userCar;
_carMake = carMake;
_carModel = carModel;
}
}
I'm using the AT_ prefix to indicate this class belongs to the Application Tier. Note that I would rather have those 3 as private properties, but for the sake of brevity I'm relaxing other guidelines in this sample code.
Now, as we read an instance of this class form the storage, we'll have to merge the proper DT_ objects in it. Again, you can have this code in the same class AT_UserCar, or decide to split it out into some separate class like this one:
public class AT_UserCarReader
{
public AT_UserCar Read(string userCarId, string carMakeId, string carModelId)
{
DT_UserCar userCar = DT_UserCar.read(userCarId);
DT_CarMake carMake = DT_CarMake.Read(carMakeId);
DT_CarModel carModel = DT_Carmodel.read(carModelId);
return new AT_UserCar(userCar, carMake, carModel);
}
}
An equivalent AT_UserCarWriter class would do the inverse operation of receiving a single AT_UserCar object and writing to the data storage 3 separate objects extracted from it: a DT_UserCar, a DT_CarMake, and a DT_CarModel.
Note that most of this code can also be automated and there is a plethora of tools that will take care of it for you.
3~ Presentation Tier:
Finally, we get to display something on screen. The important thing here is to remember that your Presentation Tier should never deal directly with the Data Tier, but only with the Application Tier.
So, for instance, if I have to retrieve a UserCar by id and display it in a web page, I could write something like this
AT_UserCar car = AT_UserCarReader.Read(userCarId, carMakeId, carModelId);
tbox_userId = car._userCar.userId;
That's, of course, a very small example, but I hope the quick run-through can help you out.
The core of 3-tier architecture (and n-tier, in general) is to separate different concerns into different layers. If you look at the example above, we targeted 3 concerns:
- talking to the data storage: we did this exclusively in the Data Tier;
- dealing with application logic such as 'merging' data from different tables into a single logical unit: we did this exclusively in the Application Tier;
- dealing with presenting to screen the data and -in more general terms- interacting with the user: we did this exclusively in the Presentation Tier.
HTH.