This reminds me of a discussion from a few months back:
One approach is sliding window table partitioning. The rows can also age off into a ReadOnly partition. All rows can be queried seamlessly, and it takes advantage of partition elimination.
Another simple approach I've used is having a scheduled process that periodically deletes rows from the primary that have aged past a certain date/time, and then leverage the OUTPUT clause to insert those same rows (or just a subset of columns) into an archive table. The entire operation is completed with a single statement and transaction.
delete from PrintJobs
output deleted.JobID, deleted.PrintDate
into PrintJobsHistory ( JobID, PrintDate )
where datediff( day, PrintDate, getdate() ) > 5;
If full query access to history is needed, a paritioned view can unionize archived rows with the primary table.
"Do not seek to follow in the footsteps of the wise. Instead, seek what they sought." - Matsuo Basho